Skip to content

Commit c11f17f

Browse files
committed
Add cryptographically strong random string functions.
1 parent e4f5e1d commit c11f17f

File tree

6 files changed

+282
-1
lines changed

6 files changed

+282
-1
lines changed

README.markdown

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,62 @@ For now, there's no way to configure a custom random generator seed.
771771

772772
Behind the scene, it makes use of the standard C function `rand()`.
773773

774+
set_secure_random_alphanum
775+
--------------------------
776+
**syntax:** *set_secure_random_alphanum $res <length>*
777+
778+
**default:** *no*
779+
780+
**context:** *location, location if*
781+
782+
**phase:** *rewrite*
783+
784+
Generates a cryptographically-strong random string `<length>` characters long with the alphabet [a-zA-Z0-9].
785+
786+
`<length>` may be between 1 and 64, inclusive.
787+
788+
For instance,
789+
790+
791+
location /test {
792+
set_secure_random_alphanum $res 32;
793+
794+
echo $res;
795+
}
796+
797+
798+
then request `GET /test` will output a string like "ivVVRP2DGaAqDmdf3Rv4ZDJ7k0gOfASz".
799+
800+
This function depends on the presence of the "/dev/urandom" device, available on most UNIX-like systems.
801+
802+
set_secure_random_lcalpha
803+
-------------------------
804+
**syntax:** *set_secure_random_lcalpha $res &lt;length&gt;*
805+
806+
**default:** *no*
807+
808+
**context:** *location, location if*
809+
810+
**phase:** *rewrite*
811+
812+
Generates a cryptographically-strong random string `<length>` characters long with the alphabet [a-z].
813+
814+
`<length>` may be between 1 and 64, inclusive.
815+
816+
For instance,
817+
818+
819+
location /test {
820+
set_secure_random_lcalpha $res 32;
821+
822+
echo $res;
823+
}
824+
825+
826+
then request `GET /test` will output a string like "kcuxcddktffsippuekhshdaclaquiusj".
827+
828+
This function depends on the presence of the "/dev/urandom" device, available on most UNIX-like systems.
829+
774830
set_local_today
775831
---------------
776832
**syntax:** *set_local_today $dst*

config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fi
77

88
ngx_addon_name=ngx_http_set_misc_module
99
HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_set_misc_module"
10-
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_base32.c $ngx_addon_dir/src/ngx_http_set_default_value.c $ngx_addon_dir/src/ngx_http_set_hashed_upstream.c $ngx_addon_dir/src/ngx_http_set_quote_sql.c $ngx_addon_dir/src/ngx_http_set_quote_json.c $ngx_addon_dir/src/ngx_http_set_unescape_uri.c $ngx_addon_dir/src/ngx_http_set_misc_module.c $ngx_addon_dir/src/ngx_http_set_escape_uri.c $ngx_addon_dir/src/ngx_http_set_hash.c $ngx_addon_dir/src/ngx_http_set_local_today.c $ngx_addon_dir/src/ngx_http_set_hex.c $ngx_addon_dir/src/ngx_http_set_base64.c $ngx_addon_dir/src/ngx_http_set_random.c"
10+
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_set_base32.c $ngx_addon_dir/src/ngx_http_set_default_value.c $ngx_addon_dir/src/ngx_http_set_hashed_upstream.c $ngx_addon_dir/src/ngx_http_set_quote_sql.c $ngx_addon_dir/src/ngx_http_set_quote_json.c $ngx_addon_dir/src/ngx_http_set_unescape_uri.c $ngx_addon_dir/src/ngx_http_set_misc_module.c $ngx_addon_dir/src/ngx_http_set_escape_uri.c $ngx_addon_dir/src/ngx_http_set_hash.c $ngx_addon_dir/src/ngx_http_set_local_today.c $ngx_addon_dir/src/ngx_http_set_hex.c $ngx_addon_dir/src/ngx_http_set_base64.c $ngx_addon_dir/src/ngx_http_set_random.c $ngx_addon_dir/src/ngx_http_set_secure_random.c"
1111
NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_set_default_value.h $ngx_addon_dir/src/ngx_http_set_hashed_upstream.h $ngx_addon_dir/src/ngx_http_set_quote_sql.h $ngx_addon_dir/src/ngx_http_set_quote_json.h $ngx_addon_dir/src/ngx_http_set_unescape_uri.h $ngx_addon_dir/src/ngx_http_set_escape_uri.h $ngx_addon_dir/src/ngx_http_set_hash.h $ngx_addon_dir/src/ngx_http_set_local_today.h $ngx_addon_dir/src/ngx_http_set_hex.h $ngx_addon_dir/src/ngx_http_set_base64.h $ngx_addon_dir/src/ngx_http_set_random.h $ngx_addon_dir/src/ngx_http_set_misc_module.h"
1212

1313
if [ $USE_OPENSSL = YES ]; then

src/ngx_http_set_misc_module.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "ngx_http_set_hmac.h"
2121
#endif
2222
#include "ngx_http_set_random.h"
23+
#include "ngx_http_set_secure_random.h"
2324

2425
#define NGX_UNESCAPE_URI_COMPONENT 0
2526

@@ -151,6 +152,20 @@ static ndk_set_var_t ngx_http_set_misc_set_random_filter = {
151152
NULL
152153
};
153154

155+
static ndk_set_var_t ngx_http_set_misc_set_secure_random_alphanum_filter = {
156+
NDK_SET_VAR_VALUE,
157+
ngx_http_set_misc_set_secure_random_alphanum,
158+
1,
159+
NULL
160+
};
161+
162+
static ndk_set_var_t ngx_http_set_misc_set_secure_random_lcalpha_filter = {
163+
NDK_SET_VAR_VALUE,
164+
ngx_http_set_misc_set_secure_random_lcalpha,
165+
1,
166+
NULL
167+
};
168+
154169
static ngx_command_t ngx_http_set_misc_commands[] = {
155170
{ ngx_string ("set_encode_base64"),
156171
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
@@ -322,6 +337,22 @@ static ngx_command_t ngx_http_set_misc_commands[] = {
322337
0,
323338
&ngx_http_set_misc_set_random_filter
324339
},
340+
{ ngx_string ("set_secure_random_alphanum"),
341+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
342+
|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
343+
ndk_set_var_value,
344+
0,
345+
0,
346+
&ngx_http_set_misc_set_secure_random_alphanum_filter
347+
},
348+
{ ngx_string ("set_secure_random_lcalpha"),
349+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
350+
|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
351+
ndk_set_var_value,
352+
0,
353+
0,
354+
&ngx_http_set_misc_set_secure_random_lcalpha_filter
355+
},
325356

326357
ngx_null_command
327358
};

src/ngx_http_set_secure_random.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#ifndef DDEBUG
2+
#define DDEBUG 0
3+
#endif
4+
#include "ddebug.h"
5+
6+
#include <ndk.h>
7+
#include "ngx_http_set_secure_random.h"
8+
#include <stdlib.h>
9+
10+
const int MAX_RANDOM_STRING = 64;
11+
12+
const int ALPHANUM = 1;
13+
const int LCALPHA = 2;
14+
15+
ngx_int_t
16+
ngx_http_set_misc_set_secure_random_common(int alphabet_type, ngx_http_request_t *r,
17+
ngx_str_t *res, ngx_http_variable_value_t *v);
18+
19+
ngx_int_t
20+
ngx_http_set_misc_set_secure_random_alphanum(ngx_http_request_t *r,
21+
ngx_str_t *res, ngx_http_variable_value_t *v)
22+
{
23+
return ngx_http_set_misc_set_secure_random_common(ALPHANUM, r, res, v);
24+
}
25+
26+
ngx_int_t
27+
ngx_http_set_misc_set_secure_random_lcalpha(ngx_http_request_t *r,
28+
ngx_str_t *res, ngx_http_variable_value_t *v)
29+
{
30+
return ngx_http_set_misc_set_secure_random_common(LCALPHA, r, res, v);
31+
}
32+
33+
ngx_int_t
34+
ngx_http_set_misc_set_secure_random_common(int alphabet_type, ngx_http_request_t *r,
35+
ngx_str_t *res, ngx_http_variable_value_t *v)
36+
{
37+
static u_char alphabet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
38+
u_char entropy[MAX_RANDOM_STRING];
39+
u_char output[MAX_RANDOM_STRING];
40+
ngx_int_t length, fd, i;
41+
ssize_t n;
42+
43+
44+
length = ngx_atoi(v->data, v->len);
45+
if (length == NGX_ERROR || length < 1 || length > MAX_RANDOM_STRING) {
46+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
47+
"set_random: bad \"length\" argument: %v", v);
48+
return NGX_ERROR;
49+
}
50+
51+
fd = ngx_open_file("/dev/urandom", NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
52+
if (fd == -1) {
53+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
54+
"set_secure_random: could not open /dev/urandom");
55+
return NGX_ERROR;
56+
}
57+
58+
n = ngx_read_fd(fd, entropy, length);
59+
if (n != length) {
60+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
61+
"set_secure_random: could not read all %d byte(s) from /dev/urandom", length);
62+
return NGX_ERROR;
63+
}
64+
65+
ngx_close_file(fd);
66+
67+
for (i = 0; i < length; i++) {
68+
if (alphabet_type == LCALPHA) {
69+
output[i] = entropy[i] % 26 + 'a';
70+
} else {
71+
output[i] = alphabet[ entropy[i] % (sizeof alphabet - 1) ];
72+
}
73+
}
74+
75+
res->data = ngx_palloc(r->pool, length);
76+
if (res->data == NULL) {
77+
return NGX_ERROR;
78+
}
79+
80+
ngx_memcpy(res->data, output, length);
81+
82+
res->len = length;
83+
84+
/* Set all required params */
85+
v->valid = 1;
86+
v->no_cacheable = 0;
87+
v->not_found = 0;
88+
89+
return NGX_OK;
90+
}

src/ngx_http_set_secure_random.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <ngx_core.h>
2+
#include <ngx_config.h>
3+
#include <ngx_http.h>
4+
5+
ngx_int_t ngx_http_set_misc_set_secure_random_alphanum(ngx_http_request_t *r,
6+
ngx_str_t *res, ngx_http_variable_value_t *v);
7+
8+
ngx_int_t ngx_http_set_misc_set_secure_random_lcalpha(ngx_http_request_t *r,
9+
ngx_str_t *res, ngx_http_variable_value_t *v);

t/secure-random.t

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# vi:filetype=perl
2+
3+
use Test::Nginx::Socket;
4+
5+
repeat_each(100);
6+
7+
plan tests => repeat_each() * 2 * blocks();
8+
9+
no_long_string();
10+
11+
run_tests();
12+
13+
#no_diff();
14+
15+
__DATA__
16+
17+
=== TEST 1: a 32-character alphanum
18+
--- config
19+
location /alphanum {
20+
set_secure_random_alphanum $res 32;
21+
22+
echo $res;
23+
}
24+
--- request
25+
GET /alphanum
26+
--- response_body_like: ^[a-zA-Z0-9]{32}$
27+
28+
=== TEST 2: a 16-character alphanum
29+
--- config
30+
location /alphanum {
31+
set_secure_random_alphanum $res 16;
32+
33+
echo $res;
34+
}
35+
--- request
36+
GET /alphanum
37+
--- response_body_like: ^[a-zA-Z0-9]{16}$
38+
39+
=== TEST 3: a 1-character alphanum
40+
--- config
41+
location /alphanum {
42+
set_secure_random_alphanum $res 1;
43+
44+
echo $res;
45+
}
46+
--- request
47+
GET /alphanum
48+
--- response_body_like: ^[a-zA-Z0-9]{1}$
49+
50+
=== TEST 4: length less than <= 0 should fail
51+
--- config
52+
location /alphanum {
53+
set_secure_random_alphanum $res 0;
54+
55+
echo $res;
56+
}
57+
--- request
58+
GET /alphanum
59+
--- response_body_like: 500 Internal Server Error
60+
--- error_code: 500
61+
62+
=== TEST 5: length less than <= 0 should fail
63+
--- config
64+
location /alphanum {
65+
set_secure_random_alphanum $res -4;
66+
67+
echo $res;
68+
}
69+
--- request
70+
GET /alphanum
71+
--- response_body_like: 500 Internal Server Error
72+
--- error_code: 500
73+
74+
=== TEST 6: non-numeric length should fail
75+
--- config
76+
location /alphanum {
77+
set_secure_random_alphanum $res bob;
78+
79+
echo $res;
80+
}
81+
--- request
82+
GET /alphanum
83+
--- response_body_like: 500 Internal Server Error
84+
--- error_code: 500
85+
86+
=== TEST 7: a 16-character lcalpha
87+
--- config
88+
location /lcalpha {
89+
set_secure_random_lcalpha $res 16;
90+
91+
echo $res;
92+
}
93+
--- request
94+
GET /lcalpha
95+
--- response_body_like: ^[a-z]{16}$

0 commit comments

Comments
 (0)