Skip to content

Commit 1a257f8

Browse files
authored
Merge pull request #23 from novashdima/master
More headers to hide, more tests, if replaced by switch
2 parents 390dc7c + 205a9ce commit 1a257f8

File tree

2 files changed

+165
-52
lines changed

2 files changed

+165
-52
lines changed

src/ngx_http_security_headers_module.c

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
#define NGX_HTTP_RP_HEADER_STRICT_ORIG_WHEN_CROSS 7
2828
#define NGX_HTTP_RP_HEADER_UNSAFE_URL 8
2929

30-
31-
3230
typedef struct {
3331
ngx_flag_t enable;
3432
ngx_flag_t hide_server_tokens;
@@ -43,6 +41,35 @@ typedef struct {
4341

4442
} ngx_http_security_headers_loc_conf_t;
4543

44+
static ngx_str_t empty_val = ngx_string("");
45+
46+
static ngx_str_t hide_headers[] = {
47+
ngx_string("x-powered-by"),
48+
ngx_string("x-cf-powered-by"),
49+
ngx_string("via"),
50+
ngx_string("x-amz-cf-id"),
51+
ngx_string("x-amz-cf-pop"),
52+
ngx_string("x-page-speed"),
53+
ngx_string("x-varnish"),
54+
ngx_string("x-cache"),
55+
ngx_string("x-cache-hits"),
56+
ngx_string("x-cache-status"),
57+
ngx_string("x-application-version"),
58+
ngx_string("x-hudson"),
59+
ngx_string("x-hudson-theme"),
60+
ngx_string("x-instance-identity"),
61+
ngx_string("x-jenkins"),
62+
ngx_string("x-jenkins-session"),
63+
ngx_string("x-envoy-upstream-service-time"),
64+
ngx_string("x-drupal-cache"),
65+
ngx_string("x-generator"),
66+
ngx_string("x-backend-server"),
67+
ngx_string("x-wix-request-id"),
68+
ngx_string("x-request-id"),
69+
ngx_string("x-sucuri-id"),
70+
ngx_string("x-hacker")
71+
};
72+
4673
static ngx_conf_enum_t ngx_http_xss_protection[] = {
4774
{ ngx_string("off"), NGX_HTTP_XSS_HEADER_OFF },
4875
{ ngx_string("on"), NGX_HTTP_XSS_HEADER_ON },
@@ -224,25 +251,11 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
224251
}
225252
h_server->hash = 0;
226253

227-
/* Hide X-Powered-By header */
228-
ngx_str_set(&key, "x-powered-by");
229-
ngx_str_set(&val, "");
230-
ngx_set_headers_out_by_search(r, &key, &val);
231-
232-
/* Hide X-Page-Speed header */
233-
ngx_str_set(&key, "x-page-speed");
234-
ngx_str_set(&val, "");
235-
ngx_set_headers_out_by_search(r, &key, &val);
236-
237-
/* Hide X-Varnish */
238-
ngx_str_set(&key, "x-varnish");
239-
ngx_str_set(&val, "");
240-
ngx_set_headers_out_by_search(r, &key, &val);
254+
size_t hide_headers_count = sizeof(hide_headers) / sizeof(hide_headers[0]);
241255

242-
/* Hide X-Application-Version */
243-
ngx_str_set(&key, "x-application-version");
244-
ngx_str_set(&val, "");
245-
ngx_set_headers_out_by_search(r, &key, &val);
256+
for (size_t i = 0; i < hide_headers_count; ++i) {
257+
ngx_set_headers_out_by_search(r, &hide_headers[i], &empty_val);
258+
}
246259
}
247260

248261
if (1 != slcf->enable) {
@@ -262,15 +275,26 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
262275
&& NGX_HTTP_SECURITY_HEADER_OMIT != slcf->xss
263276
&& ngx_http_test_content_type(r, &slcf->text_types) != NULL)
264277
{
265-
ngx_str_set(&key, "X-XSS-Protection");
266-
if (NGX_HTTP_XSS_HEADER_ON == slcf->xss) {
267-
ngx_str_set(&val, "1");
268-
} else if (NGX_HTTP_XSS_HEADER_BLOCK == slcf->xss) {
269-
ngx_str_set(&val, "1; mode=block");
270-
} else if (NGX_HTTP_XSS_HEADER_OFF == slcf->xss) {
271-
ngx_str_set(&val, "0");
278+
279+
switch (slcf->xss) {
280+
case NGX_HTTP_XSS_HEADER_ON:
281+
ngx_str_set(&val, "1");
282+
break;
283+
case NGX_HTTP_XSS_HEADER_BLOCK:
284+
ngx_str_set(&val, "1; mode=block");
285+
break;
286+
case NGX_HTTP_XSS_HEADER_OFF:
287+
ngx_str_set(&val, "0");
288+
break;
289+
default:
290+
val.len = 0;
291+
val.data = NULL;
292+
}
293+
294+
if (val.data) {
295+
ngx_str_set(&key, "X-XSS-Protection");
296+
ngx_set_headers_out_by_search(r, &key, &val);
272297
}
273-
ngx_set_headers_out_by_search(r, &key, &val);
274298
}
275299

276300
scheme_value = ngx_http_get_variable(r, &scheme, scheme_hash_key);
@@ -290,38 +314,62 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
290314
&& NGX_HTTP_SECURITY_HEADER_OMIT != slcf->fo
291315
&& ngx_http_test_content_type(r, &slcf->text_types) != NULL)
292316
{
293-
ngx_str_set(&key, "X-Frame-Options");
294-
if (NGX_HTTP_FO_HEADER_SAME == slcf->fo) {
295-
ngx_str_set(&val, "SAMEORIGIN");
296-
} else if (NGX_HTTP_FO_HEADER_DENY == slcf->fo) {
297-
ngx_str_set(&val, "DENY");
317+
318+
switch (slcf->fo) {
319+
case NGX_HTTP_FO_HEADER_SAME:
320+
ngx_str_set(&val, "SAMEORIGIN");
321+
break;
322+
case NGX_HTTP_FO_HEADER_DENY:
323+
ngx_str_set(&val, "DENY");
324+
break;
325+
default:
326+
val.len = 0;
327+
val.data = NULL;
328+
}
329+
330+
if (val.data) {
331+
ngx_str_set(&key, "X-Frame-Options");
332+
ngx_set_headers_out_by_search(r, &key, &val);
298333
}
299-
ngx_set_headers_out_by_search(r, &key, &val);
300334
}
301335

302336
/* Referrer-Policy: no-referrer-when-downgrade */
303337
if (r->headers_out.status != NGX_HTTP_NOT_MODIFIED
304338
&& NGX_HTTP_SECURITY_HEADER_OMIT != slcf->rp) {
305-
ngx_str_set(&key, "Referrer-Policy");
306339

307-
if (NGX_HTTP_RP_HEADER_NO == slcf->rp) {
308-
ngx_str_set(&val, "no-referrer");
309-
} else if (NGX_HTTP_RP_HEADER_DOWNGRADE == slcf->rp) {
310-
ngx_str_set(&val, "no-referrer-when-downgrade");
311-
} else if (NGX_HTTP_RP_HEADER_SAME_ORIGIN == slcf->rp) {
312-
ngx_str_set(&val, "same-origin");
313-
} else if (NGX_HTTP_RP_HEADER_ORIGIN == slcf->rp) {
314-
ngx_str_set(&val, "origin");
315-
} else if (NGX_HTTP_RP_HEADER_STRICT_ORIGIN == slcf->rp) {
316-
ngx_str_set(&val, "strict-origin");
317-
} else if (NGX_HTTP_RP_HEADER_ORIGIN_WHEN_CROSS == slcf->rp) {
318-
ngx_str_set(&val, "origin-when-cross-origin");
319-
} else if (NGX_HTTP_RP_HEADER_STRICT_ORIG_WHEN_CROSS == slcf->rp) {
320-
ngx_str_set(&val, "strict-origin-when-cross-origin");
321-
} else if (NGX_HTTP_RP_HEADER_UNSAFE_URL == slcf->rp) {
322-
ngx_str_set(&val, "unsafe-url");
340+
switch (slcf->rp) {
341+
case NGX_HTTP_RP_HEADER_NO:
342+
ngx_str_set(&val, "no-referrer");
343+
break;
344+
case NGX_HTTP_RP_HEADER_DOWNGRADE:
345+
ngx_str_set(&val, "no-referrer-when-downgrade");
346+
break;
347+
case NGX_HTTP_RP_HEADER_SAME_ORIGIN:
348+
ngx_str_set(&val, "same-origin");
349+
break;
350+
case NGX_HTTP_RP_HEADER_ORIGIN:
351+
ngx_str_set(&val, "origin");
352+
break;
353+
case NGX_HTTP_RP_HEADER_STRICT_ORIGIN:
354+
ngx_str_set(&val, "strict-origin");
355+
break;
356+
case NGX_HTTP_RP_HEADER_ORIGIN_WHEN_CROSS:
357+
ngx_str_set(&val, "origin-when-cross-origin");
358+
break;
359+
case NGX_HTTP_RP_HEADER_STRICT_ORIG_WHEN_CROSS:
360+
ngx_str_set(&val, "strict-origin-when-cross-origin");
361+
break;
362+
case NGX_HTTP_RP_HEADER_UNSAFE_URL:
363+
ngx_str_set(&val, "unsafe-url");
364+
break;
365+
default:
366+
val.len = 0;
367+
val.data = NULL;
323368
}
324-
ngx_set_headers_out_by_search(r, &key, &val);
369+
if (val.data) {
370+
ngx_str_set(&key, "Referrer-Policy");
371+
ngx_set_headers_out_by_search(r, &key, &val);
372+
}
325373
}
326374

327375

t/headers.t

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ x-frame-options: SAMEORIGIN
8383
hide_server_tokens on;
8484
location = /hello {
8585
security_headers on;
86+
add_header via fakeengine;
8687
8788
return 200 "hello world\n";
8889
}
@@ -98,6 +99,7 @@ hello world
9899
x-content-type-options: nosniff
99100
x-frame-options: SAMEORIGIN
100101
!Server
102+
!Via
101103
Referrer-Policy: strict-origin-when-cross-origin
102104
103105
@@ -144,6 +146,8 @@ x-frame-options: SAMEORIGIN
144146
x-xss-protection: 0
145147
referrer-policy: origin
146148
149+
150+
147151
=== TEST 8: X-Frame-Options should not be sent for CSS (even when encoding specified)
148152
--- config
149153
security_headers on;
@@ -160,3 +164,64 @@ hello world
160164
--- response_headers
161165
content-type: text/css; charset=utf-8
162166
!x-frame-options
167+
168+
169+
170+
=== TEST 9: hides common powered-by headers
171+
--- config
172+
location = /hello {
173+
security_headers on;
174+
175+
add_header X-Powered-By "PHP/8.2";
176+
add_header X-Generator "WordPress 6.5";
177+
add_header X-Jenkins "2.440";
178+
add_header X-Something-Custom "Visible";
179+
return 200 "hello world\n";
180+
}
181+
--- request
182+
GET /hello
183+
--- response_body
184+
hello world
185+
--- response_headers
186+
!x-powered-by
187+
!x-generator
188+
!x-jenkins
189+
x-something-custom: Visible
190+
191+
192+
193+
=== TEST 10: headers are visible when security_headers is off
194+
--- config
195+
location = /hello {
196+
# security_headers off (по умолчанию)
197+
add_header X-Powered-By "PHP/8.2";
198+
add_header X-Generator "WordPress";
199+
return 200 "hello world\n";
200+
}
201+
--- request
202+
GET /hello
203+
--- response_body
204+
hello world
205+
--- response_headers
206+
x-powered-by: PHP/8.2
207+
x-generator: WordPress
208+
209+
210+
211+
=== TEST 11: only hide server header
212+
--- config
213+
hide_server_tokens on;
214+
location = /hello {
215+
add_header Server "nginx";
216+
add_header X-Powered-By "PHP";
217+
add_header X-Generator "Drupal";
218+
return 200 "hello world\n";
219+
}
220+
--- request
221+
GET /hello
222+
--- response_body
223+
hello world
224+
--- response_headers
225+
!server
226+
x-powered-by: PHP
227+
x-generator: Drupal

0 commit comments

Comments
 (0)