Skip to content

Commit 785fb57

Browse files
zhuizhuhaomengagentzh
authored andcommitted
feature: ngx.req.set_uri_args() now automatically escapes control and whitespace characters if the query-string is provided directly.
Signed-off-by: Yichun Zhang (agentzh) <yichun@openresty.com>
1 parent 99a5a6b commit 785fb57

File tree

4 files changed

+236
-3
lines changed

4 files changed

+236
-3
lines changed

README.markdown

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4552,7 +4552,12 @@ or a Lua table holding the query arguments' key-value pairs, as in
45524552
ngx.req.set_uri_args({ a = 3, b = "hello world" })
45534553
```
45544554

4555-
where in the latter case, this method will escape argument keys and values according to the URI escaping rule.
4555+
In the former case, i.e., when the whole query-strng is provided directly,
4556+
the input Lua string should already be well-formed with the URI encoding.
4557+
For security considerations, his method will autoamticaly escape any control and
4558+
whitespace characters (ASCII code 0x00 ~ 0x32 and 0x7F) in the Lua string.
4559+
4560+
In the latter case, this method will escape argument keys and values according to the URI escaping rule.
45564561

45574562
Multi-value arguments are also supported:
45584563

doc/HttpLuaModule.wiki

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3791,7 +3791,12 @@ or a Lua table holding the query arguments' key-value pairs, as in
37913791
ngx.req.set_uri_args({ a = 3, b = "hello world" })
37923792
</geshi>
37933793
3794-
where in the latter case, this method will escape argument keys and values according to the URI escaping rule.
3794+
In the former case, i.e., when the whole query-strng is provided directly,
3795+
the input Lua string should already be well-formed with the URI encoding.
3796+
For security considerations, his method will autoamticaly escape any control and
3797+
whitespace characters (ASCII code 0x00 ~ 0x32 and 0x7F) in the Lua string.
3798+
3799+
In the latter case, this method will escape argument keys and values according to the URI escaping rule.
37953800
37963801
Multi-value arguments are also supported:
37973802

src/ngx_http_lua_args.c

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,66 @@ static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L);
1919
static int ngx_http_lua_ngx_req_get_post_args(lua_State *L);
2020

2121

22+
uintptr_t
23+
ngx_http_lua_escape_args(u_char *dst, u_char *src, size_t size)
24+
{
25+
ngx_uint_t n;
26+
static u_char hex[] = "0123456789ABCDEF";
27+
28+
/* %00-%20 %7F*/
29+
30+
static uint32_t escape[] = {
31+
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
32+
33+
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
34+
0x00000001, /* 0000 0000 0000 0000 0000 0000 0000 0001 */
35+
36+
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
37+
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
38+
39+
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
40+
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
41+
42+
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
43+
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
44+
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
45+
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
46+
};
47+
48+
if (dst == NULL) {
49+
50+
/* find the number of the characters to be escaped */
51+
52+
n = 0;
53+
54+
while (size) {
55+
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
56+
n++;
57+
}
58+
src++;
59+
size--;
60+
}
61+
62+
return (uintptr_t) n;
63+
}
64+
65+
while (size) {
66+
if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
67+
*dst++ = '%';
68+
*dst++ = hex[*src >> 4];
69+
*dst++ = hex[*src & 0xf];
70+
src++;
71+
72+
} else {
73+
*dst++ = *src++;
74+
}
75+
size--;
76+
}
77+
78+
return (uintptr_t) dst;
79+
}
80+
81+
2282
static int
2383
ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
2484
{
@@ -27,6 +87,7 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
2787
const char *msg;
2888
size_t len;
2989
u_char *p;
90+
uintptr_t escape;
3091

3192
if (lua_gettop(L) != 1) {
3293
return luaL_error(L, "expecting 1 argument but seen %d",
@@ -42,7 +103,6 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
42103

43104
switch (lua_type(L, 1)) {
44105
case LUA_TNUMBER:
45-
case LUA_TSTRING:
46106
p = (u_char *) lua_tolstring(L, 1, &len);
47107

48108
args.data = ngx_palloc(r->pool, len);
@@ -55,6 +115,32 @@ ngx_http_lua_ngx_req_set_uri_args(lua_State *L)
55115
args.len = len;
56116
break;
57117

118+
case LUA_TSTRING:
119+
p = (u_char *) lua_tolstring(L, 1, &len);
120+
121+
escape = ngx_http_lua_escape_args(NULL, p, len);
122+
if (escape > 0) {
123+
args.len = len + 2 * escape;
124+
args.data = ngx_palloc(r->pool, args.len);
125+
if (args.data == NULL) {
126+
return NGX_ERROR;
127+
}
128+
129+
ngx_http_lua_escape_args(args.data, p, len);
130+
131+
} else {
132+
args.data = ngx_palloc(r->pool, len);
133+
if (args.data == NULL) {
134+
return luaL_error(L, "no memory");
135+
}
136+
137+
ngx_memcpy(args.data, p, len);
138+
139+
args.len = len;
140+
}
141+
142+
break;
143+
58144
case LUA_TTABLE:
59145
ngx_http_lua_process_args_option(r, L, 1, &args);
60146

t/030-uri-args-with-ctrl.t

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# vim:set ft= ts=4 sw=4 et fdm=marker:
2+
use Test::Nginx::Socket::Lua;
3+
4+
log_level('warn');
5+
6+
repeat_each(2);
7+
8+
plan tests => repeat_each() * (blocks() * 2 + 3);
9+
10+
no_root_location();
11+
12+
no_long_string();
13+
run_tests();
14+
15+
__DATA__
16+
17+
=== TEST 1: rewrite args (string with \r)
18+
--- config
19+
location /foo {
20+
rewrite_by_lua_block {
21+
ngx.req.set_uri_args("a\rb")
22+
}
23+
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;
24+
}
25+
location /echo {
26+
content_by_lua_block {
27+
ngx.say(ngx.var.request_uri);
28+
}
29+
}
30+
--- request
31+
GET /foo?world
32+
--- error_code: 200
33+
--- response_body
34+
/echo?a%0Db
35+
36+
37+
38+
=== TEST 2: rewrite args (string with \n)
39+
--- config
40+
location /foo {
41+
rewrite_by_lua_block {
42+
ngx.req.set_uri_args("a\nb")
43+
}
44+
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;
45+
}
46+
location /echo {
47+
content_by_lua_block {
48+
ngx.say(ngx.var.request_uri);
49+
}
50+
}
51+
--- request
52+
GET /foo?world
53+
--- response_body
54+
/echo?a%0Ab
55+
56+
57+
58+
=== TEST 3: rewrite args (string with \0)
59+
--- config
60+
location /foo {
61+
rewrite_by_lua_block {
62+
ngx.req.set_uri_args("a\0b")
63+
}
64+
proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/echo;
65+
}
66+
location /echo {
67+
content_by_lua_block {
68+
ngx.say(ngx.var.request_uri);
69+
}
70+
}
71+
--- request
72+
GET /foo?world
73+
--- response_body
74+
/echo?a%00b
75+
76+
77+
78+
=== TEST 4: rewrite args (string arg with 'lang=中文')
79+
ngx.req.set_uri_args with string argument should be carefully encoded.
80+
For backward compatibility, we are allowed to pass such parameters.
81+
--- config
82+
location /foo {
83+
rewrite_by_lua_block {
84+
ngx.req.set_uri_args("lang=中文")
85+
}
86+
content_by_lua_block {
87+
ngx.say(ngx.var.arg_lang)
88+
}
89+
}
90+
--- request
91+
GET /foo?world
92+
--- response_body
93+
中文
94+
--- no_error_log
95+
[error]
96+
97+
98+
99+
=== TEST 5: rewrite args (string arg with '语言=chinese')
100+
ngx.req.set_uri_args with string argument should be carefully encoded.
101+
For backward compatibility, we are allowed to pass such parameters.
102+
--- config
103+
location /foo {
104+
rewrite_by_lua_block {
105+
ngx.req.set_uri_args("语言=chinese")
106+
}
107+
content_by_lua_block {
108+
ngx.say(ngx.var.arg_语言)
109+
}
110+
}
111+
--- request
112+
GET /foo?world
113+
--- response_body
114+
chinese
115+
--- no_error_log
116+
[error]
117+
118+
119+
120+
=== TEST 6: rewrite args (string arg with '语言=中文')
121+
ngx.req.set_uri_args with string argument should be carefully encoded.
122+
For backward compatibility, we are allowed to pass such parameters.
123+
--- config
124+
location /foo {
125+
rewrite_by_lua_block {
126+
ngx.req.set_uri_args("语言=中文")
127+
}
128+
content_by_lua_block {
129+
ngx.say(ngx.var.arg_语言)
130+
}
131+
}
132+
--- request
133+
GET /foo?world
134+
--- response_body
135+
中文
136+
--- no_error_log
137+
[error]

0 commit comments

Comments
 (0)