Skip to content

Commit 4e48acf

Browse files
authored
Merge pull request #3 from AirisX/feature/samesite_flag
Feature/samesite flag
2 parents 068d668 + 5bdf21f commit 4e48acf

File tree

2 files changed

+98
-10
lines changed

2 files changed

+98
-10
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ Earlier versions is not tested.
3434

3535
```Nginx
3636
location / {
37-
set_cookie_flag Secret HttpOnly secure;
37+
set_cookie_flag Secret HttpOnly secure SameSite;
3838
set_cookie_flag * HttpOnly;
39-
set_cookie_flag SessionID secure;
39+
set_cookie_flag SessionID SameSite=Lax secure;
40+
set_cookie_flag SiteToken SameSite=Strict;
4041
}
4142
```
4243

4344
## Description
44-
This module for Nginx allows to set the flags "**HttpOnly**" and "**secure**" for cookies in the "*Set-Cookie*" response headers.
45+
This module for Nginx allows to set the flags "**HttpOnly**", "**secure**" and "**SameSite**" for cookies in the "*Set-Cookie*" response headers.
4546
The register of letters for the flags doesn't matter as it will be converted to the correct value. The order of cookie declaration among multiple directives doesn't matter too.
4647
It is possible to set a default value using symbol "*". In this case flags will be added to the all cookies if no other value for them is overriden.
4748

@@ -51,7 +52,7 @@ It is possible to set a default value using symbol "*". In this case flags will
5152

5253
-| -
5354
--- | ---
54-
**Syntax** | **set_cookie_flag** \<cookie_name\|*\> [HttpOnly\|secure] [HttpOnly\|secure];
55+
**Syntax** | **set_cookie_flag** \<cookie_name\|*\> [HttpOnly] [secure] [SameSite\|SameSite=[Lax\|Strict]];
5556
**Default** | -
5657
**Context** | server, location
5758

ngx_http_cookie_flag_filter_module.c

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22
#include <ngx_core.h>
33
#include <ngx_http.h>
44

5+
#define NUM_FLAGS 3
6+
#define MIN_ARGS 3
7+
58
typedef struct {
69
ngx_str_t cookie_name;
710
ngx_flag_t httponly;
811
ngx_flag_t secure;
12+
ngx_flag_t samesite;
13+
ngx_flag_t samesite_lax;
14+
ngx_flag_t samesite_strict;
915
} ngx_http_cookie_t;
1016

1117
typedef struct {
@@ -18,6 +24,40 @@ static char *ngx_http_cookie_flag_filter_merge_loc_conf(ngx_conf_t *cf, void *pa
1824
static ngx_int_t ngx_http_cookie_flag_filter_init(ngx_conf_t *cf);
1925
static ngx_int_t ngx_http_cookie_flag_filter_append(ngx_http_request_t *r, ngx_http_cookie_t *flag, ngx_table_elt_t *header);
2026
static ngx_int_t ngx_http_cookie_flag_filter_handler(ngx_http_request_t *r);
27+
static size_t ngx_char_pos(ngx_str_t str, u_char ch);
28+
static u_char *ngx_get_arg_name(ngx_pool_t *pool, ngx_str_t src);
29+
30+
static size_t ngx_char_pos(ngx_str_t str, u_char ch) {
31+
32+
char *pos = NULL;
33+
size_t ind = 0;
34+
pos = strchr((char *) str.data, (int) ch);
35+
if (pos != NULL) {
36+
ind = (size_t) (pos - (char *) str.data);
37+
}
38+
return ind;
39+
40+
}
41+
42+
static u_char *ngx_get_arg_name(ngx_pool_t *pool, ngx_str_t src) {
43+
44+
u_char *dst;
45+
size_t pos;
46+
47+
pos = ngx_char_pos(src, '=');
48+
49+
if(pos) {
50+
dst = ngx_pnalloc(pool, pos + 1);
51+
if (dst == NULL) {
52+
return NULL;
53+
}
54+
ngx_memcpy(dst, src.data, pos);
55+
return dst;
56+
} else {
57+
return src.data;
58+
}
59+
60+
}
2161

2262
static ngx_command_t ngx_http_cookie_flag_filter_commands[] = {
2363

@@ -73,19 +113,27 @@ ngx_http_cookie_flag_filter_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
73113

74114
ngx_http_cookie_t *cookie, tmp;
75115
ngx_str_t *value;
76-
ngx_uint_t i;
116+
ngx_uint_t i, j;
77117

78118
value = cf->args->elts;
79119

80-
if (cf->args->nelts > 4 || cf->args->nelts < 3) {
120+
if (cf->args->nelts > (NUM_FLAGS + 2) || cf->args->nelts < MIN_ARGS) {
81121
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "The number of arguments is incorrect");
82122
return NGX_CONF_ERROR;
83123
}
84124

85-
if (cf->args->nelts == 4) {
86-
if (ngx_strncasecmp(value[2].data, value[3].data, value[3].len) == 0) {
87-
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Duplicate flag \"%V\" detected", &value[3]);
88-
return NGX_CONF_ERROR;
125+
// check on duplication
126+
if (cf->args->nelts > MIN_ARGS) {
127+
for (i = MIN_ARGS; i < cf->args->nelts; i++) {
128+
for (j = MIN_ARGS - 1; j < i; j++) {
129+
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, "filter http_cookie_flag - comparasion \"%V\" and \"%V\"", &value[i], &value[j]);
130+
u_char *first = ngx_get_arg_name(cf->pool, value[j]);
131+
u_char *second = ngx_get_arg_name(cf->pool, value[i]);
132+
if (ngx_strcasecmp(first, second) == 0) {
133+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Duplicate flag \"%s\" (%V) detected", second, &value[i]);
134+
return NGX_CONF_ERROR;
135+
}
136+
}
89137
}
90138
}
91139

@@ -114,13 +162,22 @@ ngx_http_cookie_flag_filter_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
114162
cookie->cookie_name.len = value[1].len;
115163
cookie->httponly = 0;
116164
cookie->secure = 0;
165+
cookie->samesite = 0;
166+
cookie->samesite_lax = 0;
167+
cookie->samesite_strict = 0;
117168

118169
// normalize and check 2nd and 3rd parameters
119170
for (i = 2; i < cf->args->nelts; i++) {
120171
if (ngx_strncasecmp(value[i].data, (u_char *) "httponly", 8) == 0 && value[i].len == 8) {
121172
cookie->httponly = 1;
122173
} else if (ngx_strncasecmp(value[i].data, (u_char *) "secure", 6) == 0 && value[i].len == 6) {
123174
cookie->secure = 1;
175+
} else if (ngx_strncasecmp(value[i].data, (u_char *) "samesite", 8) == 0 && value[i].len == 8) {
176+
cookie->samesite = 1;
177+
} else if (ngx_strncasecmp(value[i].data, (u_char *) "samesite=lax", 12) == 0 && value[i].len == 12) {
178+
cookie->samesite_lax = 1;
179+
} else if (ngx_strncasecmp(value[i].data, (u_char *) "samesite=strict", 15) == 0 && value[i].len == 15) {
180+
cookie->samesite_strict = 1;
124181
} else {
125182
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "The parameter value \"%V\" is incorrect", &value[i]);
126183
return NGX_CONF_ERROR;
@@ -200,6 +257,36 @@ ngx_http_cookie_flag_filter_append(ngx_http_request_t *r, ngx_http_cookie_t *coo
200257
header->value.len = tmp.len;
201258
}
202259

260+
if (cookie->samesite == 1 && ngx_strcasestrn(header->value.data, "; SameSite", 10 - 1) == NULL) {
261+
tmp.data = ngx_pnalloc(r->pool, header->value.len + sizeof("; SameSite") - 1);
262+
if (tmp.data == NULL) {
263+
return NGX_ERROR;
264+
}
265+
tmp.len = ngx_sprintf(tmp.data, "%V; SameSite", &header->value) - tmp.data;
266+
header->value.data = tmp.data;
267+
header->value.len = tmp.len;
268+
}
269+
270+
if (cookie->samesite_lax == 1 && ngx_strcasestrn(header->value.data, "; SameSite=Lax", 14 - 1) == NULL) {
271+
tmp.data = ngx_pnalloc(r->pool, header->value.len + sizeof("; SameSite=Lax") - 1);
272+
if (tmp.data == NULL) {
273+
return NGX_ERROR;
274+
}
275+
tmp.len = ngx_sprintf(tmp.data, "%V; SameSite=Lax", &header->value) - tmp.data;
276+
header->value.data = tmp.data;
277+
header->value.len = tmp.len;
278+
}
279+
280+
if (cookie->samesite_strict == 1 && ngx_strcasestrn(header->value.data, "; SameSite=Strict", 17 - 1) == NULL) {
281+
tmp.data = ngx_pnalloc(r->pool, header->value.len + sizeof("; SameSite=Strict") - 1);
282+
if (tmp.data == NULL) {
283+
return NGX_ERROR;
284+
}
285+
tmp.len = ngx_sprintf(tmp.data, "%V; SameSite=Strict", &header->value) - tmp.data;
286+
header->value.data = tmp.data;
287+
header->value.len = tmp.len;
288+
}
289+
203290
return NGX_OK;
204291
}
205292

0 commit comments

Comments
 (0)