Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add cryptographically strong random string functions.

  • Loading branch information...
commit c11f17f09bb1a4743a4034b4dd760c2117de069f 1 parent e4f5e1d
@jeremywohl jeremywohl authored
View
56 README.markdown
@@ -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*
View
2  config
@@ -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
View
31 src/ngx_http_set_misc_module.c
@@ -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
@@ -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
@@ -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
};
View
90 src/ngx_http_set_secure_random.c
@@ -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;
+}
View
9 src/ngx_http_set_secure_random.h
@@ -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);
View
95 t/secure-random.t
@@ -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}$
Please sign in to comment.
Something went wrong with that request. Please try again.