Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@ Special `omit` value will disable sending the header by the module.
Controls inclusion and value of [`Referrer-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) header.
Special `omit` value will disable sending the header by the module.

### `security_headers_coop`

- **syntax**: `security_headers_coop unsafe-none | same-origin-allow-popups | same-origin | omit`
- **default**: `omit`
- **context**: `http`, `server`, `location`

Controls inclusion and value of [`Cross-Origin-Opener-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) header.
This header allows you to ensure a top-level document does not share a browsing context group with cross-origin documents.
Special `omit` value (default) will disable sending the header by the module.

### `security_headers_corp`

- **syntax**: `security_headers_corp same-site | same-origin | cross-origin | omit`
- **default**: `omit`
- **context**: `http`, `server`, `location`

Controls inclusion and value of [`Cross-Origin-Resource-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy) header.
This header conveys a desire that the browser blocks no-cors cross-origin/cross-site requests to the given resource.
Special `omit` value (default) will disable sending the header by the module.

### `security_headers_coep`

- **syntax**: `security_headers_coep unsafe-none | require-corp | omit`
- **default**: `omit`
- **context**: `http`, `server`, `location`

Controls inclusion and value of [`Cross-Origin-Embedder-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) header.
This header prevents a document from loading any cross-origin resources that don't explicitly grant the document permission.
Special `omit` value (default) will disable sending the header by the module.

## Install

We highly recommend installing using packages, where available,
Expand Down
161 changes: 161 additions & 0 deletions src/ngx_http_security_headers_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@
#define NGX_HTTP_RP_HEADER_STRICT_ORIG_WHEN_CROSS 7
#define NGX_HTTP_RP_HEADER_UNSAFE_URL 8

/* Cross-Origin-Opener-Policy header */
#define NGX_HTTP_COOP_HEADER_UNSAFE_NONE 1
#define NGX_HTTP_COOP_HEADER_SAME_ORIGIN_POPUPS 2
#define NGX_HTTP_COOP_HEADER_SAME_ORIGIN 3

/* Cross-Origin-Resource-Policy header */
#define NGX_HTTP_CORP_HEADER_SAME_SITE 1
#define NGX_HTTP_CORP_HEADER_SAME_ORIGIN 2
#define NGX_HTTP_CORP_HEADER_CROSS_ORIGIN 3

/* Cross-Origin-Embedder-Policy header */
#define NGX_HTTP_COEP_HEADER_UNSAFE_NONE 1
#define NGX_HTTP_COEP_HEADER_REQUIRE_CORP 2

typedef struct {
ngx_flag_t enable;
ngx_flag_t hide_server_tokens;
Expand All @@ -35,6 +49,9 @@ typedef struct {
ngx_uint_t xss;
ngx_uint_t fo;
ngx_uint_t rp;
ngx_uint_t coop;
ngx_uint_t corp;
ngx_uint_t coep;

ngx_hash_t text_types;
ngx_array_t *text_types_keys;
Expand Down Expand Up @@ -113,6 +130,51 @@ static ngx_conf_enum_t ngx_http_referrer_policy[] = {
{ ngx_null_string, 0 }
};

static ngx_conf_enum_t ngx_http_cross_origin_opener_policy[] = {
{ ngx_string("unsafe-none"),
NGX_HTTP_COOP_HEADER_UNSAFE_NONE },

{ ngx_string("same-origin-allow-popups"),
NGX_HTTP_COOP_HEADER_SAME_ORIGIN_POPUPS },

{ ngx_string("same-origin"),
NGX_HTTP_COOP_HEADER_SAME_ORIGIN },

{ ngx_string("omit"),
NGX_HTTP_SECURITY_HEADER_OMIT },

{ ngx_null_string, 0 }
};

static ngx_conf_enum_t ngx_http_cross_origin_resource_policy[] = {
{ ngx_string("same-site"),
NGX_HTTP_CORP_HEADER_SAME_SITE },

{ ngx_string("same-origin"),
NGX_HTTP_CORP_HEADER_SAME_ORIGIN },

{ ngx_string("cross-origin"),
NGX_HTTP_CORP_HEADER_CROSS_ORIGIN },

{ ngx_string("omit"),
NGX_HTTP_SECURITY_HEADER_OMIT },

{ ngx_null_string, 0 }
};

static ngx_conf_enum_t ngx_http_cross_origin_embedder_policy[] = {
{ ngx_string("unsafe-none"),
NGX_HTTP_COEP_HEADER_UNSAFE_NONE },

{ ngx_string("require-corp"),
NGX_HTTP_COEP_HEADER_REQUIRE_CORP },

{ ngx_string("omit"),
NGX_HTTP_SECURITY_HEADER_OMIT },

{ ngx_null_string, 0 }
};

static ngx_int_t ngx_http_security_headers_filter(ngx_http_request_t *r);
static void *ngx_http_security_headers_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_security_headers_merge_loc_conf(ngx_conf_t *cf,
Expand Down Expand Up @@ -173,6 +235,27 @@ static ngx_command_t ngx_http_security_headers_commands[] = {
offsetof(ngx_http_security_headers_loc_conf_t, rp),
ngx_http_referrer_policy },

{ ngx_string("security_headers_coop"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_security_headers_loc_conf_t, coop),
ngx_http_cross_origin_opener_policy },

{ ngx_string("security_headers_corp"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_security_headers_loc_conf_t, corp),
ngx_http_cross_origin_resource_policy },

{ ngx_string("security_headers_coep"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_security_headers_loc_conf_t, coep),
ngx_http_cross_origin_embedder_policy },

{ ngx_string("security_headers_text_types"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_types_slot,
Expand Down Expand Up @@ -372,6 +455,75 @@ ngx_http_security_headers_filter(ngx_http_request_t *r)
}
}

/* Cross-Origin-Opener-Policy */
if (r->headers_out.status != NGX_HTTP_NOT_MODIFIED
&& NGX_HTTP_SECURITY_HEADER_OMIT != slcf->coop) {

switch (slcf->coop) {
case NGX_HTTP_COOP_HEADER_UNSAFE_NONE:
ngx_str_set(&val, "unsafe-none");
break;
case NGX_HTTP_COOP_HEADER_SAME_ORIGIN_POPUPS:
ngx_str_set(&val, "same-origin-allow-popups");
break;
case NGX_HTTP_COOP_HEADER_SAME_ORIGIN:
ngx_str_set(&val, "same-origin");
break;
default:
val.len = 0;
val.data = NULL;
}
if (val.data) {
ngx_str_set(&key, "Cross-Origin-Opener-Policy");
ngx_set_headers_out_by_search(r, &key, &val);
}
}

/* Cross-Origin-Resource-Policy */
if (r->headers_out.status != NGX_HTTP_NOT_MODIFIED
&& NGX_HTTP_SECURITY_HEADER_OMIT != slcf->corp) {

switch (slcf->corp) {
case NGX_HTTP_CORP_HEADER_SAME_SITE:
ngx_str_set(&val, "same-site");
break;
case NGX_HTTP_CORP_HEADER_SAME_ORIGIN:
ngx_str_set(&val, "same-origin");
break;
case NGX_HTTP_CORP_HEADER_CROSS_ORIGIN:
ngx_str_set(&val, "cross-origin");
break;
default:
val.len = 0;
val.data = NULL;
}
if (val.data) {
ngx_str_set(&key, "Cross-Origin-Resource-Policy");
ngx_set_headers_out_by_search(r, &key, &val);
}
}

/* Cross-Origin-Embedder-Policy */
if (r->headers_out.status != NGX_HTTP_NOT_MODIFIED
&& NGX_HTTP_SECURITY_HEADER_OMIT != slcf->coep) {

switch (slcf->coep) {
case NGX_HTTP_COEP_HEADER_UNSAFE_NONE:
ngx_str_set(&val, "unsafe-none");
break;
case NGX_HTTP_COEP_HEADER_REQUIRE_CORP:
ngx_str_set(&val, "require-corp");
break;
default:
val.len = 0;
val.data = NULL;
}
if (val.data) {
ngx_str_set(&key, "Cross-Origin-Embedder-Policy");
ngx_set_headers_out_by_search(r, &key, &val);
}
}



/* proceed to the next handler in chain */
Expand All @@ -392,6 +544,9 @@ ngx_http_security_headers_create_loc_conf(ngx_conf_t *cf)
conf->xss = NGX_CONF_UNSET_UINT;
conf->fo = NGX_CONF_UNSET_UINT;
conf->rp = NGX_CONF_UNSET_UINT;
conf->coop = NGX_CONF_UNSET_UINT;
conf->corp = NGX_CONF_UNSET_UINT;
conf->coep = NGX_CONF_UNSET_UINT;
conf->enable = NGX_CONF_UNSET;
conf->hide_server_tokens = NGX_CONF_UNSET_UINT;
conf->hsts_preload = NGX_CONF_UNSET_UINT;
Expand Down Expand Up @@ -425,6 +580,12 @@ ngx_http_security_headers_merge_loc_conf(ngx_conf_t *cf, void *parent,
NGX_HTTP_FO_HEADER_SAME);
ngx_conf_merge_uint_value(conf->rp, prev->rp,
NGX_HTTP_RP_HEADER_STRICT_ORIG_WHEN_CROSS);
ngx_conf_merge_uint_value(conf->coop, prev->coop,
NGX_HTTP_SECURITY_HEADER_OMIT);
ngx_conf_merge_uint_value(conf->corp, prev->corp,
NGX_HTTP_SECURITY_HEADER_OMIT);
ngx_conf_merge_uint_value(conf->coep, prev->coep,
NGX_HTTP_SECURITY_HEADER_OMIT);

return NGX_CONF_OK;
}
Expand Down
Loading