Skip to content

Commit 068d668

Browse files
committed
Initial commit
0 parents  commit 068d668

File tree

4 files changed

+378
-0
lines changed

4 files changed

+378
-0
lines changed

LICENSE

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
BSD 2-Clause License
2+
3+
Copyright (c) 2017, Airis777
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
The Nginx module for adding cookie flag
2+
==========
3+
4+
[![License](http://img.shields.io/badge/license-BSD-brightgreen.svg)](https://github.com/Airis777/nginx_cookie_flag_module/blob/master/LICENSE)
5+
6+
The Nginx module for adding cookie flag
7+
8+
## Dependencies
9+
* [nginx](http://nginx.org)
10+
11+
## Compatibility
12+
* 1.11.x (last tested: 1.11.2)
13+
14+
Earlier versions is not tested.
15+
16+
## Installation
17+
18+
1. Clone the git repository.
19+
20+
```
21+
git clone git://github.com:AirisX/nginx_cookie_flag_module.git
22+
```
23+
24+
2. Add the module to the build configuration by adding
25+
`--add-module=/path/to/nginx_cookie_flag_module`
26+
or
27+
`--add-dynamic-module=/path/to/nginx_cookie_flag_module`
28+
29+
3. Build the nginx binary.
30+
31+
4. Install the nginx binary.
32+
33+
## Synopsis
34+
35+
```Nginx
36+
location / {
37+
set_cookie_flag Secret HttpOnly secure;
38+
set_cookie_flag * HttpOnly;
39+
set_cookie_flag SessionID secure;
40+
}
41+
```
42+
43+
## Description
44+
This module for Nginx allows to set the flags "**HttpOnly**" and "**secure**" for cookies in the "*Set-Cookie*" response headers.
45+
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.
46+
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.
47+
48+
## Directives
49+
50+
### set_cookie_flag
51+
52+
-| -
53+
--- | ---
54+
**Syntax** | **set_cookie_flag** \<cookie_name\|*\> [HttpOnly\|secure] [HttpOnly\|secure];
55+
**Default** | -
56+
**Context** | server, location
57+
58+
Description: Add flag to desired cookie.
59+
60+
## Author
61+
Anton Saraykin [<Airisenator@gmail.com>]

config

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
ngx_addon_name=ngx_http_cookie_flag_filter_module
2+
3+
if test -n "$ngx_module_link"; then
4+
ngx_module_type=HTTP_FILTER
5+
ngx_module_name=$ngx_addon_name
6+
ngx_module_srcs="$ngx_addon_dir/$ngx_addon_name.c"
7+
8+
. auto/module
9+
else
10+
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $ngx_addon_name"
11+
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/$ngx_addon_name.c"
12+
fi
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
#include <ngx_config.h>
2+
#include <ngx_core.h>
3+
#include <ngx_http.h>
4+
5+
typedef struct {
6+
ngx_str_t cookie_name;
7+
ngx_flag_t httponly;
8+
ngx_flag_t secure;
9+
} ngx_http_cookie_t;
10+
11+
typedef struct {
12+
ngx_array_t *cookies;
13+
} ngx_http_cookie_flag_filter_loc_conf_t;
14+
15+
static char *ngx_http_cookie_flag_filter_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
16+
static void *ngx_http_cookie_flag_filter_create_loc_conf(ngx_conf_t *cf);
17+
static char *ngx_http_cookie_flag_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
18+
static ngx_int_t ngx_http_cookie_flag_filter_init(ngx_conf_t *cf);
19+
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);
20+
static ngx_int_t ngx_http_cookie_flag_filter_handler(ngx_http_request_t *r);
21+
22+
static ngx_command_t ngx_http_cookie_flag_filter_commands[] = {
23+
24+
/* set cookie flag directive */
25+
{ ngx_string("set_cookie_flag"),
26+
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
27+
ngx_http_cookie_flag_filter_cmd,
28+
NGX_HTTP_LOC_CONF_OFFSET,
29+
0,
30+
NULL
31+
},
32+
33+
ngx_null_command
34+
35+
};
36+
37+
static ngx_http_module_t ngx_http_cookie_flag_filter_module_ctx = {
38+
NULL, /* preconfiguration */
39+
ngx_http_cookie_flag_filter_init, /* postconfiguration */
40+
41+
NULL, /* create main configuration */
42+
NULL, /* init main configuration */
43+
44+
NULL, /* create server configuration */
45+
NULL, /* merge server configuration */
46+
47+
ngx_http_cookie_flag_filter_create_loc_conf, /* create location configuration */
48+
ngx_http_cookie_flag_filter_merge_loc_conf /* merge location configuration */
49+
};
50+
51+
ngx_module_t ngx_http_cookie_flag_filter_module = {
52+
NGX_MODULE_V1,
53+
&ngx_http_cookie_flag_filter_module_ctx, /* module context */
54+
ngx_http_cookie_flag_filter_commands, /* module directives */
55+
NGX_HTTP_MODULE, /* module type */
56+
NULL, /* init master */
57+
NULL, /* init module */
58+
NULL, /* init process */
59+
NULL, /* init thread */
60+
NULL, /* exit thread */
61+
NULL, /* exit process */
62+
NULL, /* exit master */
63+
NGX_MODULE_V1_PADDING
64+
};
65+
66+
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
67+
68+
static char *
69+
ngx_http_cookie_flag_filter_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
70+
{
71+
72+
ngx_http_cookie_flag_filter_loc_conf_t *flcf = conf;
73+
74+
ngx_http_cookie_t *cookie, tmp;
75+
ngx_str_t *value;
76+
ngx_uint_t i;
77+
78+
value = cf->args->elts;
79+
80+
if (cf->args->nelts > 4 || cf->args->nelts < 3) {
81+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "The number of arguments is incorrect");
82+
return NGX_CONF_ERROR;
83+
}
84+
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;
89+
}
90+
}
91+
92+
if (flcf->cookies == NULL) {
93+
flcf->cookies = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cookie_t));
94+
if (flcf->cookies == NULL) {
95+
return NGX_CONF_ERROR;
96+
}
97+
} else {
98+
// check whether cookie name has already set
99+
cookie = flcf->cookies->elts;
100+
for (i = 0; i < flcf->cookies->nelts; i++) {
101+
if (ngx_strncasecmp(cookie[i].cookie_name.data, value[1].data, value[1].len) == 0) {
102+
return "The cookie value has already set in previous directives";
103+
}
104+
}
105+
}
106+
107+
cookie = ngx_array_push(flcf->cookies);
108+
if (cookie == NULL) {
109+
return NGX_CONF_ERROR;
110+
}
111+
112+
// set cookie name
113+
cookie->cookie_name.data = value[1].data;
114+
cookie->cookie_name.len = value[1].len;
115+
cookie->httponly = 0;
116+
cookie->secure = 0;
117+
118+
// normalize and check 2nd and 3rd parameters
119+
for (i = 2; i < cf->args->nelts; i++) {
120+
if (ngx_strncasecmp(value[i].data, (u_char *) "httponly", 8) == 0 && value[i].len == 8) {
121+
cookie->httponly = 1;
122+
} else if (ngx_strncasecmp(value[i].data, (u_char *) "secure", 6) == 0 && value[i].len == 6) {
123+
cookie->secure = 1;
124+
} else {
125+
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "The parameter value \"%V\" is incorrect", &value[i]);
126+
return NGX_CONF_ERROR;
127+
}
128+
}
129+
130+
// move default settings to the end of array
131+
cookie = flcf->cookies->elts;
132+
for (i = 0; i < flcf->cookies->nelts; i++) {
133+
if (ngx_strncasecmp(cookie[i].cookie_name.data, (u_char *) "*", 1) == 0 && i < flcf->cookies->nelts - 1) {
134+
tmp = cookie[flcf->cookies->nelts - 1];
135+
cookie[flcf->cookies->nelts - 1] = cookie[i];
136+
cookie[i] = tmp;
137+
}
138+
}
139+
140+
return NGX_CONF_OK;
141+
}
142+
143+
static void *
144+
ngx_http_cookie_flag_filter_create_loc_conf(ngx_conf_t *cf)
145+
{
146+
ngx_http_cookie_flag_filter_loc_conf_t *conf;
147+
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_cookie_flag_filter_loc_conf_t));
148+
if (conf == NULL) {
149+
return NULL;
150+
}
151+
152+
return conf;
153+
}
154+
155+
static char *
156+
ngx_http_cookie_flag_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
157+
{
158+
ngx_http_cookie_flag_filter_loc_conf_t *prev = parent;
159+
ngx_http_cookie_flag_filter_loc_conf_t *conf = child;
160+
161+
if (conf->cookies == NULL) {
162+
conf->cookies = prev->cookies;
163+
}
164+
165+
return NGX_CONF_OK;
166+
}
167+
168+
static ngx_int_t
169+
ngx_http_cookie_flag_filter_init(ngx_conf_t *cf)
170+
{
171+
172+
ngx_http_next_header_filter = ngx_http_top_header_filter;
173+
ngx_http_top_header_filter = ngx_http_cookie_flag_filter_handler;
174+
175+
return NGX_OK;
176+
}
177+
178+
static ngx_int_t
179+
ngx_http_cookie_flag_filter_append(ngx_http_request_t *r, ngx_http_cookie_t *cookie, ngx_table_elt_t *header)
180+
{
181+
ngx_str_t tmp;
182+
183+
if (cookie->httponly == 1 && ngx_strcasestrn(header->value.data, "; HttpOnly", 10 - 1) == NULL) {
184+
tmp.data = ngx_pnalloc(r->pool, header->value.len + sizeof("; HttpOnly") - 1);
185+
if (tmp.data == NULL) {
186+
return NGX_ERROR;
187+
}
188+
tmp.len = ngx_sprintf(tmp.data, "%V; HttpOnly", &header->value) - tmp.data;
189+
header->value.data = tmp.data;
190+
header->value.len = tmp.len;
191+
}
192+
193+
if (cookie->secure == 1 && ngx_strcasestrn(header->value.data, "; secure", 8 - 1) == NULL) {
194+
tmp.data = ngx_pnalloc(r->pool, header->value.len + sizeof("; secure") - 1);
195+
if (tmp.data == NULL) {
196+
return NGX_ERROR;
197+
}
198+
tmp.len = ngx_sprintf(tmp.data, "%V; secure", &header->value) - tmp.data;
199+
header->value.data = tmp.data;
200+
header->value.len = tmp.len;
201+
}
202+
203+
return NGX_OK;
204+
}
205+
206+
static ngx_int_t
207+
ngx_http_cookie_flag_filter_handler(ngx_http_request_t *r)
208+
{
209+
ngx_http_cookie_flag_filter_loc_conf_t *flcf;
210+
ngx_http_cookie_t *cookie;
211+
ngx_uint_t i, j;
212+
ngx_list_part_t *part;
213+
ngx_table_elt_t *header;
214+
215+
flcf = ngx_http_get_module_loc_conf(r, ngx_http_cookie_flag_filter_module);
216+
217+
if (flcf->cookies == NULL) {
218+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "filter http_cookie_flag is disabled");
219+
return ngx_http_next_header_filter(r);
220+
}
221+
222+
cookie = flcf->cookies->elts;
223+
224+
if (flcf->cookies->nelts != 0) {
225+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "filter http_cookie_flag is enabled");
226+
}
227+
228+
// Checking whether Set-Cookie header is present
229+
part = &r->headers_out.headers.part;
230+
header = part->elts;
231+
for (i = 0; /* void */; i++) {
232+
if (i >= part->nelts) {
233+
if (part->next == NULL) {
234+
break;
235+
}
236+
237+
part = part->next;
238+
header = part->elts;
239+
i = 0;
240+
}
241+
242+
if (ngx_strncasecmp(header[i].key.data, (u_char *) "set-cookie", 10) == 0) {
243+
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "filter http_cookie_flag - before: \"%V: %V\"", &header[i].key, &header[i].value);
244+
245+
// for each security cookie we check whether preset it within Set-Cookie value. If not then we append.
246+
for (j = 0; j < flcf->cookies->nelts; j++) {
247+
248+
if (ngx_strncasecmp(cookie[j].cookie_name.data, (u_char *) "*", 1) != 0) {
249+
// append "=" to the security cookie name. The result will be something like "cookie_name="
250+
char *cookie_name = ngx_pnalloc(r->pool, sizeof("=") - 1 + cookie[j].cookie_name.len);
251+
if (cookie_name == NULL) {
252+
return NGX_ERROR;
253+
}
254+
strcpy(cookie_name, (char *) cookie[j].cookie_name.data);
255+
strcat(cookie_name, "=");
256+
257+
// if Set-Cookie contains a cookie from settings
258+
if (ngx_strcasestrn(header[i].value.data, cookie_name, strlen(cookie_name) - 1) != NULL) {
259+
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "filter http_cookie_flag - add flags for cookie \"%V\"", &cookie[j].cookie_name);
260+
ngx_int_t res = ngx_http_cookie_flag_filter_append(r, &cookie[j], &header[i]);
261+
if (res != NGX_OK) {
262+
return NGX_ERROR;
263+
}
264+
break; // otherwise default value will be added
265+
}
266+
} else if (ngx_strncasecmp(cookie[j].cookie_name.data, (u_char *) "*", 1) == 0) {
267+
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "filter http_cookie_flag - add default cookie flags");
268+
ngx_int_t res = ngx_http_cookie_flag_filter_append(r, &cookie[j], &header[i]);
269+
if (res != NGX_OK) {
270+
return NGX_ERROR;
271+
}
272+
}
273+
}
274+
275+
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "filter http_cookie_flag - after: \"%V: %V\"", &header[i].key, &header[i].value);
276+
}
277+
}
278+
279+
return ngx_http_next_header_filter(r);
280+
}

0 commit comments

Comments
 (0)