1919
2020typedef struct {
2121 ngx_flag_t enable ;
22+ ngx_flag_t hide_server_tokens ;
2223
2324 ngx_uint_t xss ;
2425 ngx_uint_t fo ;
@@ -67,6 +68,13 @@ static ngx_command_t ngx_http_security_headers_commands[] = {
6768 offsetof( ngx_http_security_headers_loc_conf_t , enable ),
6869 NULL },
6970
71+ { ngx_string ( "hide_server_tokens" ),
72+ NGX_HTTP_MAIN_CONF |NGX_HTTP_SRV_CONF |NGX_HTTP_LOC_CONF |NGX_CONF_FLAG ,
73+ ngx_conf_set_flag_slot ,
74+ NGX_HTTP_LOC_CONF_OFFSET ,
75+ offsetof(ngx_http_security_headers_loc_conf_t , hide_server_tokens ),
76+ NULL },
77+
7078 { ngx_string ("security_headers_nosniff_types" ),
7179 NGX_HTTP_MAIN_CONF |NGX_HTTP_SRV_CONF |NGX_HTTP_LOC_CONF |NGX_CONF_1MORE ,
7280 ngx_http_types_slot ,
@@ -134,12 +142,31 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
134142{
135143 ngx_http_security_headers_loc_conf_t * slcf ;
136144
145+ ngx_table_elt_t * h_server ;
146+
137147 ngx_str_t key ;
138148 ngx_str_t val ;
139149
140-
141150 slcf = ngx_http_get_module_loc_conf (r , ngx_http_security_headers_module );
142151
152+ if (1 == slcf -> hide_server_tokens ) {
153+ /* Hide the Server header */
154+ h_server = r -> headers_out .server ;
155+ if (h_server == NULL ) {
156+ h_server = ngx_list_push (& r -> headers_out .headers );
157+ if (h_server == NULL ) {
158+ return NGX_ERROR ;
159+ }
160+ r -> headers_out .server = h_server ;
161+ }
162+ h_server -> hash = 0 ;
163+
164+ /* Hide X-Powered-By header */
165+ ngx_str_set (& key , "x-powered-by" );
166+ ngx_str_set (& val , "" );
167+ ngx_set_headers_out_by_search (r , & key , & val );
168+ }
169+
143170 if (1 != slcf -> enable ) {
144171 return ngx_http_next_header_filter (r );
145172 }
@@ -169,6 +196,14 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
169196 ngx_set_headers_out_by_search (r , & key , & val );
170197 }
171198
199+ #if (NGX_HTTP_SSL )
200+ if (r -> connection -> ssl ) {
201+ ngx_str_set (& key , "Strict-Transport-Security" );
202+ ngx_str_set (& val , "max-age=63072000; includeSubDomains; preload" );
203+ ngx_set_headers_out_by_search (r , & key , & val );
204+ }
205+ #endif
206+
172207 /* Add X-Frame-Options */
173208 if (r -> headers_out .status != NGX_HTTP_NOT_MODIFIED
174209 && NGX_HTTP_SECURITY_HEADER_OMIT != slcf -> fo )
@@ -182,19 +217,16 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
182217 ngx_set_headers_out_by_search (r , & key , & val );
183218 }
184219
185- /* Find X-Powered-By header */
186- ngx_str_set (& key , "x-powered-by" );
187- ngx_str_set (& val , "" );
188- ngx_set_headers_out_by_search (r , & key , & val );
220+ /* Referrer-Policy: no-referrer-when-downgrade */
221+ if (r -> headers_out .status != NGX_HTTP_NOT_MODIFIED ) {
222+ ngx_str_set (& key , "Referrer-Policy" );
223+ ngx_str_set (& val , "no-referrer-when-downgrade" );
224+ ngx_set_headers_out_by_search (r , & key , & val );
225+ }
189226
190227
191- /* Deal with Server header */
192- ngx_str_set (& key , "server" );
193- ngx_str_set (& val , "" );
194- ngx_set_headers_out_by_search (r , & key , & val );
195228
196229 /* proceed to the next handler in chain */
197-
198230 return ngx_http_next_header_filter (r );
199231}
200232
@@ -212,6 +244,7 @@ ngx_http_security_headers_create_loc_conf(ngx_conf_t *cf)
212244 conf -> xss = NGX_CONF_UNSET_UINT ;
213245 conf -> fo = NGX_CONF_UNSET_UINT ;
214246 conf -> enable = NGX_CONF_UNSET ;
247+ conf -> hide_server_tokens = NGX_CONF_UNSET_UINT ;
215248
216249 return conf ;
217250}
@@ -225,6 +258,8 @@ ngx_http_security_headers_merge_loc_conf(ngx_conf_t *cf, void *parent,
225258 ngx_http_security_headers_loc_conf_t * conf = child ;
226259
227260 ngx_conf_merge_value ( conf -> enable , prev -> enable , 0 );
261+ ngx_conf_merge_value (conf -> hide_server_tokens ,
262+ prev -> hide_server_tokens , 0 );
228263
229264 if (ngx_http_merge_types (cf , & conf -> types_keys , & conf -> nosniff_types ,
230265 & prev -> types_keys , & prev -> nosniff_types ,
@@ -259,63 +294,74 @@ ngx_set_headers_out_by_search(ngx_http_request_t *r,
259294 ngx_str_t * key , ngx_str_t * value )
260295{
261296 ngx_list_part_t * part ;
262- ngx_table_elt_t * hi ;
263297 ngx_uint_t i ;
264- ngx_table_elt_t * h = NULL ;
298+ ngx_table_elt_t * h ;
299+ ngx_flag_t matched = 0 ;
265300
266- /*
267- Get the first part of the list. There is usual only one part.
268- */
269301 part = & r -> headers_out .headers .part ;
270- hi = part -> elts ;
302+ h = part -> elts ;
303+
304+ for (i = 0 ; /* void */ ; i ++ ) {
271305
272- /*
273- Headers list array may consist of more than one part,
274- so loop through all of it
275- */
276- for (i = 0 ; /* void */ ; i ++ ) {
277306 if (i >= part -> nelts ) {
278307 if (part -> next == NULL ) {
279- /* The last part, search is done. */
280308 break ;
281309 }
282310
283311 part = part -> next ;
284- hi = part -> elts ;
312+ h = part -> elts ;
285313 i = 0 ;
286314 }
287315
288- if (hi [i ].hash == 0 ) {
316+ if (h [i ].hash == 0 ) {
289317 continue ;
290318 }
291319
292- /*
293- Just compare the lengths and then the names case insensitively.
294- */
295- if (key -> len != hi [i ].key .len
296- || ngx_strcasecmp (key -> data , hi [i ].key .data ) != 0 )
320+ if (h [i ].key .len == key -> len
321+ && ngx_strncasecmp (h [i ].key .data , key -> data ,
322+ h [i ].key .len ) == 0 )
297323 {
298- /* This header doesn't match. */
299- continue ;
324+ goto matched ;
300325 }
301326
302- h = hi ;
303- break ;
327+ /* not matched */
328+ continue ;
329+ matched :
330+
331+ if (value -> len == 0 || matched ) {
332+ h [i ].value .len = 0 ;
333+ h [i ].hash = 0 ;
334+ } else {
335+ h [i ].value = * value ;
336+ h [i ].hash = 1 ;
337+ }
338+
339+ matched = 1 ;
304340 }
305- if (h == NULL ) {
306- h = ngx_list_push (& r -> headers_out .headers );
341+
342+ if (matched ){
343+ return NGX_OK ;
307344 }
345+
346+ /* XXX we still need to create header slot even if the value
347+ * is empty because some builtin headers like Last-Modified
348+ * relies on this to get cleared */
349+
350+ h = ngx_list_push (& r -> headers_out .headers );
308351 if (h == NULL ) {
309352 return NGX_ERROR ;
310353 }
311- h -> key = * key ;
312- h -> value = * value ;
313- if (value -> len == 0 ) {
354+
355+ if (value -> len == 0 ) {
314356 h -> hash = 0 ;
357+
315358 } else {
316359 h -> hash = 1 ;
317360 }
318361
362+ h -> key = * key ;
363+ h -> value = * value ;
364+
319365 h -> lowcase_key = ngx_pnalloc (r -> pool , h -> key .len );
320366 if (h -> lowcase_key == NULL ) {
321367 return NGX_ERROR ;
@@ -325,3 +371,4 @@ ngx_set_headers_out_by_search(ngx_http_request_t *r,
325371
326372 return NGX_OK ;
327373}
374+
0 commit comments