Skip to content

Add cryptographically strong random string functions. #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 10, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,62 @@ For now, there's no way to configure a custom random generator seed.

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

set_secure_random_alphanum
--------------------------
**syntax:** *set_secure_random_alphanum $res <length>*

**default:** *no*

**context:** *location, location if*

**phase:** *rewrite*

Generates a cryptographically-strong random string `<length>` characters long with the alphabet [a-zA-Z0-9].

`<length>` may be between 1 and 64, inclusive.

For instance,


location /test {
set_secure_random_alphanum $res 32;

echo $res;
}


then request `GET /test` will output a string like "ivVVRP2DGaAqDmdf3Rv4ZDJ7k0gOfASz".

This function depends on the presence of the "/dev/urandom" device, available on most UNIX-like systems.

set_secure_random_lcalpha
-------------------------
**syntax:** *set_secure_random_lcalpha $res &lt;length&gt;*

**default:** *no*

**context:** *location, location if*

**phase:** *rewrite*

Generates a cryptographically-strong random string `<length>` characters long with the alphabet [a-z].

`<length>` may be between 1 and 64, inclusive.

For instance,


location /test {
set_secure_random_lcalpha $res 32;

echo $res;
}


then request `GET /test` will output a string like "kcuxcddktffsippuekhshdaclaquiusj".

This function depends on the presence of the "/dev/urandom" device, available on most UNIX-like systems.

set_local_today
---------------
**syntax:** *set_local_today $dst*
Expand Down
2 changes: 1 addition & 1 deletion config
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fi

ngx_addon_name=ngx_http_set_misc_module
HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_set_misc_module"
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_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"
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"

if [ $USE_OPENSSL = YES ]; then
Expand Down
31 changes: 31 additions & 0 deletions src/ngx_http_set_misc_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "ngx_http_set_hmac.h"
#endif
#include "ngx_http_set_random.h"
#include "ngx_http_set_secure_random.h"

#define NGX_UNESCAPE_URI_COMPONENT 0

Expand Down Expand Up @@ -151,6 +152,20 @@ static ndk_set_var_t ngx_http_set_misc_set_random_filter = {
NULL
};

static ndk_set_var_t ngx_http_set_misc_set_secure_random_alphanum_filter = {
NDK_SET_VAR_VALUE,
ngx_http_set_misc_set_secure_random_alphanum,
1,
NULL
};

static ndk_set_var_t ngx_http_set_misc_set_secure_random_lcalpha_filter = {
NDK_SET_VAR_VALUE,
ngx_http_set_misc_set_secure_random_lcalpha,
1,
NULL
};

static ngx_command_t ngx_http_set_misc_commands[] = {
{ ngx_string ("set_encode_base64"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
Expand Down Expand Up @@ -322,6 +337,22 @@ static ngx_command_t ngx_http_set_misc_commands[] = {
0,
&ngx_http_set_misc_set_random_filter
},
{ ngx_string ("set_secure_random_alphanum"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
ndk_set_var_value,
0,
0,
&ngx_http_set_misc_set_secure_random_alphanum_filter
},
{ ngx_string ("set_secure_random_lcalpha"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF
|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
ndk_set_var_value,
0,
0,
&ngx_http_set_misc_set_secure_random_lcalpha_filter
},

ngx_null_command
};
Expand Down
90 changes: 90 additions & 0 deletions src/ngx_http_set_secure_random.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"

#include <ndk.h>
#include "ngx_http_set_secure_random.h"
#include <stdlib.h>

const int MAX_RANDOM_STRING = 64;

const int ALPHANUM = 1;
const int LCALPHA = 2;

ngx_int_t
ngx_http_set_misc_set_secure_random_common(int alphabet_type, ngx_http_request_t *r,
ngx_str_t *res, ngx_http_variable_value_t *v);

ngx_int_t
ngx_http_set_misc_set_secure_random_alphanum(ngx_http_request_t *r,
ngx_str_t *res, ngx_http_variable_value_t *v)
{
return ngx_http_set_misc_set_secure_random_common(ALPHANUM, r, res, v);
}

ngx_int_t
ngx_http_set_misc_set_secure_random_lcalpha(ngx_http_request_t *r,
ngx_str_t *res, ngx_http_variable_value_t *v)
{
return ngx_http_set_misc_set_secure_random_common(LCALPHA, r, res, v);
}

ngx_int_t
ngx_http_set_misc_set_secure_random_common(int alphabet_type, ngx_http_request_t *r,
ngx_str_t *res, ngx_http_variable_value_t *v)
{
static u_char alphabet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
u_char entropy[MAX_RANDOM_STRING];
u_char output[MAX_RANDOM_STRING];
ngx_int_t length, fd, i;
ssize_t n;


length = ngx_atoi(v->data, v->len);
if (length == NGX_ERROR || length < 1 || length > MAX_RANDOM_STRING) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"set_random: bad \"length\" argument: %v", v);
return NGX_ERROR;
}

fd = ngx_open_file("/dev/urandom", NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == -1) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"set_secure_random: could not open /dev/urandom");
return NGX_ERROR;
}

n = ngx_read_fd(fd, entropy, length);
if (n != length) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"set_secure_random: could not read all %d byte(s) from /dev/urandom", length);
return NGX_ERROR;
}

ngx_close_file(fd);

for (i = 0; i < length; i++) {
if (alphabet_type == LCALPHA) {
output[i] = entropy[i] % 26 + 'a';
} else {
output[i] = alphabet[ entropy[i] % (sizeof alphabet - 1) ];
}
}

res->data = ngx_palloc(r->pool, length);
if (res->data == NULL) {
return NGX_ERROR;
}

ngx_memcpy(res->data, output, length);

res->len = length;

/* Set all required params */
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;

return NGX_OK;
}
9 changes: 9 additions & 0 deletions src/ngx_http_set_secure_random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <ngx_core.h>
#include <ngx_config.h>
#include <ngx_http.h>

ngx_int_t ngx_http_set_misc_set_secure_random_alphanum(ngx_http_request_t *r,
ngx_str_t *res, ngx_http_variable_value_t *v);

ngx_int_t ngx_http_set_misc_set_secure_random_lcalpha(ngx_http_request_t *r,
ngx_str_t *res, ngx_http_variable_value_t *v);
95 changes: 95 additions & 0 deletions t/secure-random.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# vi:filetype=perl

use Test::Nginx::Socket;

repeat_each(100);

plan tests => repeat_each() * 2 * blocks();

no_long_string();

run_tests();

#no_diff();

__DATA__

=== TEST 1: a 32-character alphanum
--- config
location /alphanum {
set_secure_random_alphanum $res 32;

echo $res;
}
--- request
GET /alphanum
--- response_body_like: ^[a-zA-Z0-9]{32}$

=== TEST 2: a 16-character alphanum
--- config
location /alphanum {
set_secure_random_alphanum $res 16;

echo $res;
}
--- request
GET /alphanum
--- response_body_like: ^[a-zA-Z0-9]{16}$

=== TEST 3: a 1-character alphanum
--- config
location /alphanum {
set_secure_random_alphanum $res 1;

echo $res;
}
--- request
GET /alphanum
--- response_body_like: ^[a-zA-Z0-9]{1}$

=== TEST 4: length less than <= 0 should fail
--- config
location /alphanum {
set_secure_random_alphanum $res 0;

echo $res;
}
--- request
GET /alphanum
--- response_body_like: 500 Internal Server Error
--- error_code: 500

=== TEST 5: length less than <= 0 should fail
--- config
location /alphanum {
set_secure_random_alphanum $res -4;

echo $res;
}
--- request
GET /alphanum
--- response_body_like: 500 Internal Server Error
--- error_code: 500

=== TEST 6: non-numeric length should fail
--- config
location /alphanum {
set_secure_random_alphanum $res bob;

echo $res;
}
--- request
GET /alphanum
--- response_body_like: 500 Internal Server Error
--- error_code: 500

=== TEST 7: a 16-character lcalpha
--- config
location /lcalpha {
set_secure_random_lcalpha $res 16;

echo $res;
}
--- request
GET /lcalpha
--- response_body_like: ^[a-z]{16}$