Skip to content

Commit 881bf91

Browse files
doujiang24thibaultcha
authored andcommitted
bugfix: ensured arguments of APIs mutating response headers do not contain '\r' and '\n' characters.
Signed-off-by: Thibault Charbonnier <[email protected]>
1 parent 246ec8a commit 881bf91

File tree

7 files changed

+257
-2
lines changed

7 files changed

+257
-2
lines changed

README.markdown

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4186,6 +4186,11 @@ to be returned when reading `ngx.header.Foo`.
41864186

41874187
Note that `ngx.header` is not a normal Lua table and as such, it is not possible to iterate through it using the Lua `ipairs` function.
41884188

4189+
Note: `HEADER` and `VALUE` will be truncated if they
4190+
contain the `\r` or `\n` characters. The truncated values
4191+
will contain all characters up to (and excluding) the first occurrence of
4192+
`\r` or `\n`.
4193+
41894194
For reading *request* headers, use the [ngx.req.get_headers](#ngxreqget_headers) function instead.
41904195

41914196
[Back to TOC](#nginx-api-for-lua)
@@ -5151,6 +5156,13 @@ ngx.redirect
51515156

51525157
Issue an `HTTP 301` or `302` redirection to `uri`.
51535158

5159+
Notice: the `uri` should not contains `\r` or `\n`, otherwise, the characters after `\r` or `\n` will be truncated, including the `\r` or `\n` bytes themself.
5160+
5161+
The `uri` argument will be truncated if it contains the
5162+
`\r` or `\n` characters. The truncated value will contain
5163+
all characters up to (and excluding) the first occurrence of `\r` or
5164+
`\n`.
5165+
51545166
The optional `status` parameter specifies the HTTP status code to be used. The following status codes are supported right now:
51555167

51565168
* `301`

doc/HttpLuaModule.wiki

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3468,6 +3468,11 @@ to be returned when reading <code>ngx.header.Foo</code>.
34683468
34693469
Note that <code>ngx.header</code> is not a normal Lua table and as such, it is not possible to iterate through it using the Lua <code>ipairs</code> function.
34703470
3471+
Note: <code>HEADER</code> and <code>VALUE</code> will be truncated if they
3472+
contain the <code>\r</code> or <code>\n</code> characters. The truncated values
3473+
will contain all characters up to (and excluding) the first occurrence of
3474+
<code>\r</code> or <code>\n</code>.
3475+
34713476
For reading ''request'' headers, use the [[#ngx.req.get_headers|ngx.req.get_headers]] function instead.
34723477
34733478
== ngx.resp.get_headers ==
@@ -4302,6 +4307,13 @@ It is recommended that a coding style that combines this method call with the <c
43024307
43034308
Issue an <code>HTTP 301</code> or <code>302</code> redirection to <code>uri</code>.
43044309
4310+
Notice: the <code>uri</code> should not contains <code>\r</code> or <code>\n</code>, otherwise, the characters after <code>\r</code> or <code>\n</code> will be truncated, including the <code>\r</code> or <code>\n</code> bytes themself.
4311+
4312+
The <code>uri</code> argument will be truncated if it contains the
4313+
<code>\r</code> or <code>\n</code> characters. The truncated value will contain
4314+
all characters up to (and excluding) the first occurrence of <code>\r</code> or
4315+
<code>\n</code>.
4316+
43054317
The optional <code>status</code> parameter specifies the HTTP status code to be used. The following status codes are supported right now:
43064318
43074319
* <code>301</code>

src/ngx_http_lua_control.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ ngx_http_lua_ngx_redirect(lua_State *L)
248248
"the headers");
249249
}
250250

251+
len = ngx_http_lua_safe_header_value_len(p, len);
252+
251253
uri = ngx_palloc(r->pool, len);
252254
if (uri == NULL) {
253255
return luaL_error(L, "no memory");

src/ngx_http_lua_headers_out.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,10 @@ ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx,
491491

492492
dd("set header value: %.*s", (int) value.len, value.data);
493493

494+
key.len = ngx_http_lua_safe_header_value_len(key.data, key.len);
495+
496+
value.len = ngx_http_lua_safe_header_value_len(value.data, value.len);
497+
494498
hv.hash = ngx_hash_key_lc(key.data, key.len);
495499
hv.key = key;
496500

src/ngx_http_lua_util.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,21 @@ void ngx_http_lua_set_sa_restart(ngx_log_t *log);
260260
}
261261

262262

263+
static ngx_inline size_t
264+
ngx_http_lua_safe_header_value_len(u_char *str, size_t len)
265+
{
266+
size_t i;
267+
268+
for (i = 0; i < len; i++, str++) {
269+
if (*str == '\r' || *str == '\n') {
270+
return i;
271+
}
272+
}
273+
274+
return len;
275+
}
276+
277+
263278
static ngx_inline void
264279
ngx_http_lua_init_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
265280
{

t/016-resp-header.t

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use Test::Nginx::Socket::Lua;
88

99
repeat_each(2);
1010

11-
plan tests => repeat_each() * (blocks() * 3 + 59);
11+
plan tests => repeat_each() * (blocks() * 3 + 73);
1212

1313
#no_diff();
1414
no_long_string();
@@ -1963,3 +1963,139 @@ foo
19631963
Content-Type: application/json
19641964
--- no_error_log
19651965
[error]
1966+
1967+
1968+
1969+
=== TEST 87: truncates value after '\r'
1970+
--- config
1971+
location = /t {
1972+
content_by_lua_block {
1973+
ngx.header.header = "value\rfoo:bar\nbar:foo"
1974+
ngx.say("foo")
1975+
}
1976+
}
1977+
--- request
1978+
GET /t
1979+
--- response_headers
1980+
header: value
1981+
foo:
1982+
bar:
1983+
--- no_error_log
1984+
[error]
1985+
1986+
1987+
1988+
=== TEST 88: truncates value after '\n'
1989+
--- config
1990+
location = /t {
1991+
content_by_lua_block {
1992+
ngx.header.header = "value\nfoo:bar\rbar:foo"
1993+
ngx.say("foo")
1994+
}
1995+
}
1996+
--- request
1997+
GET /t
1998+
--- response_headers
1999+
header: value
2000+
foo:
2001+
bar:
2002+
--- no_error_log
2003+
[error]
2004+
2005+
2006+
2007+
=== TEST 89: truncates key after '\r'
2008+
--- config
2009+
location = /t {
2010+
content_by_lua_block {
2011+
ngx.header["header: value\rfoo:bar\nbar:foo"] = "xx"
2012+
ngx.say("foo")
2013+
}
2014+
}
2015+
--- request
2016+
GET /t
2017+
--- response_headers
2018+
header: value: xx
2019+
foo:
2020+
bar:
2021+
--- no_error_log
2022+
[error]
2023+
2024+
2025+
2026+
=== TEST 90: truncates key after '\n'
2027+
--- config
2028+
location = /t {
2029+
content_by_lua_block {
2030+
ngx.header["header: value\nfoo:bar\rbar:foo"] = "xx"
2031+
ngx.say("foo")
2032+
}
2033+
}
2034+
--- request
2035+
GET /t
2036+
--- response_headers
2037+
header: value: xx
2038+
foo:
2039+
bar:
2040+
--- no_error_log
2041+
[error]
2042+
2043+
2044+
2045+
=== TEST 91: truncates key after '\r' as the first character
2046+
--- config
2047+
location = /t {
2048+
content_by_lua_block {
2049+
ngx.header["\rheader: value\rfoo:bar\nbar:foo"] = "xx"
2050+
ngx.say("foo")
2051+
}
2052+
}
2053+
--- request
2054+
GET /t
2055+
--- response_headers
2056+
header:
2057+
foo:
2058+
bar:
2059+
--- no_error_log
2060+
[error]
2061+
2062+
2063+
2064+
=== TEST 92: truncates key after '\n' as the first character
2065+
--- config
2066+
location = /t {
2067+
content_by_lua_block {
2068+
ngx.header["\nheader: value\nfoo:bar\rbar:foo"] = "xx"
2069+
ngx.say("foo")
2070+
}
2071+
}
2072+
--- request
2073+
GET /t
2074+
--- response_headers
2075+
header:
2076+
foo:
2077+
bar:
2078+
--- no_error_log
2079+
[error]
2080+
2081+
2082+
2083+
=== TEST 93: truncates multiple values if they contain '\r' or '\n'
2084+
--- config
2085+
location = /t {
2086+
content_by_lua_block {
2087+
ngx.header["foo"] = {
2088+
"foo\nxx:bar",
2089+
"bar\rxxx:foo",
2090+
}
2091+
ngx.say("foo")
2092+
}
2093+
}
2094+
--- request
2095+
GET /t
2096+
--- response_headers
2097+
foo: foo, bar
2098+
xx:
2099+
xxx:
2100+
--- no_error_log
2101+
[error]

t/022-redirect.t

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use Test::Nginx::Socket::Lua;
99
repeat_each(2);
1010
#repeat_each(1);
1111

12-
plan tests => repeat_each() * (blocks() * 3 + 2);
12+
plan tests => repeat_each() * (blocks() * 3 + 8);
1313

1414
#no_diff();
1515
#no_long_string();
@@ -320,3 +320,77 @@ GET /read
320320
--- response_headers
321321
Location: http://agentzh.org/foo?a=b&c=d
322322
--- error_code: 308
323+
324+
325+
326+
=== TEST 18: truncates uri after '\r'
327+
--- config
328+
location = /t {
329+
content_by_lua_block {
330+
ngx.redirect("http://agentzh.org/foo\rfoo:bar\nbar:foo");
331+
ngx.say("hi")
332+
}
333+
}
334+
--- request
335+
GET /t
336+
--- response_headers
337+
Location: http://agentzh.org/foo
338+
foo:
339+
bar:
340+
--- response_body_like: 302 Found
341+
--- error_code: 302
342+
343+
344+
345+
=== TEST 19: truncates uri after '\n'
346+
--- config
347+
location = /t {
348+
content_by_lua_block {
349+
ngx.redirect("http://agentzh.org/foo\nfoo:bar\rbar:foo");
350+
ngx.say("hi")
351+
}
352+
}
353+
--- request
354+
GET /t
355+
--- response_headers
356+
Location: http://agentzh.org/foo
357+
foo:
358+
bar:
359+
--- response_body_like: 302 Found
360+
--- error_code: 302
361+
362+
363+
364+
=== TEST 20: truncates uri with '\n' as the first character
365+
--- config
366+
location = /t {
367+
content_by_lua_block {
368+
ngx.redirect("\nfoo:http://agentzh.org/foo");
369+
ngx.say("hi")
370+
}
371+
}
372+
--- request
373+
GET /t
374+
--- response_headers
375+
Location:
376+
foo:
377+
--- response_body_like: 302 Found
378+
--- error_code: 302
379+
380+
381+
382+
=== TEST 21: truncates uri with '\r' as the first character
383+
--- config
384+
location = /t {
385+
content_by_lua_block {
386+
ngx.redirect("\rfoo:http://agentzh.org/foo");
387+
ngx.say("hi")
388+
}
389+
}
390+
--- request
391+
GET /t
392+
--- response_headers
393+
Location:
394+
foo:
395+
--- response_body_like: 302 Found
396+
--- error_code: 302

0 commit comments

Comments
 (0)