Welcome! Log In Create A New Profile

Advanced

[PATCH] Nginx secure link module with HMAC construction

April 28, 2012 04:50AM
Hello,

Attached is the proposed patch to http_secure_link module. With the
patch, the security and functionality of the module is extended. First
of all, the secure token is created using much more secure HMAC
construction with an arbitrary hash algorithm supported by OpenSSL,
e.g., md5, sha1, sha256, sha512. Secure token is created in the
standard way as in RFC2104, that is, H(secret_key XOR opad,
H(secret_key XOR ipad, message)) instead of a simple MD5(secret_key,
message, expire). Message to be hashed is defined by
secure_link_hmac_message, secret_key is given by
secure_link_hmac_secret, and hashing algorithm H is defined by
secure_link_hmac_algorithm. The expiration timestamp can be either
appended to secret key, or message to be hashed, or both.
Configuration example below.

location ^~ /files/ {
secure_link $arg_st,$arg_e;
secure_link_hmac_secret my_secret_key$arg_e;
secure_link_hmac_message $uri;
secure_link_hmac_algorithm sha256;

if ($secure_link = "") {
return 403;
}

if ($secure_link = "0") {
return 410;
}

rewrite ^/files/(.$)$ /files/$1 break;
}

Application side can use a standard hash_hmac function to generate
hash, which then needs to be base64 encoded. Example in PHP

$expire = time() + 3600;
$secret = "my_secret_key" . $expire;
$algo = "sha256";
$path = "/files/top_secret.pdf";
$hashmac = base64_encode(hash_hmac($algo,$path,$secret,true));
$hashmac = strtr($hashmac,"+/","-_"));
$hashmac = str_replace("=","",$hashmac);
$host = $_SERVER['HTTP_HOST'];
$loc = "https://" . $host . "/files/top_secret.pdf" . "?st=" .
$hashmac . "&e=" . $expire;

Patch below.

--- ngx_http_secure_link_module.c 2012-01-18 17:07:43.000000000 +0200
+++ ngx_http_hmac_secure_link_module.c 2012-04-28 10:19:00.000000000 +0300
@@ -9,12 +9,17 @@
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_md5.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>


typedef struct {
ngx_http_complex_value_t *variable;
ngx_http_complex_value_t *md5;
+ ngx_http_complex_value_t *hmac_message;
+ ngx_http_complex_value_t *hmac_secret;
ngx_str_t secret;
+ ngx_str_t hmac_algorithm;
} ngx_http_secure_link_conf_t;


@@ -26,6 +31,9 @@
static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,
ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
uintptr_t data);
+static ngx_int_t ngx_http_secure_link_hmac_variable(ngx_http_request_t *r,
+ ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
+ uintptr_t data);
static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);
@@ -57,6 +65,27 @@
offsetof(ngx_http_secure_link_conf_t, secret),
NULL },

+ { ngx_string("secure_link_hmac_message"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_secure_link_conf_t, hmac_message),
+ NULL },
+
+ { ngx_string("secure_link_hmac_secret"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_secure_link_conf_t, hmac_secret),
+ NULL },
+
+ { ngx_string("secure_link_hmac_algorithm"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_secure_link_conf_t, hmac_algorithm),
+ NULL },
+
ngx_null_command
};

@@ -115,6 +144,10 @@
return ngx_http_secure_link_old_variable(r, conf, v, data);
}

+ if (conf->hmac_algorithm.len) {
+ return ngx_http_secure_link_hmac_variable(r, conf, v, data);
+ }
+
if (conf->variable == NULL || conf->md5 == NULL) {
goto not_found;
}
@@ -266,6 +299,107 @@
return NGX_OK;
}

+static ngx_int_t
+ngx_http_secure_link_hmac_variable(ngx_http_request_t *r,
+ ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ u_char *p, *last;
+ ngx_str_t val, hash, key;
+ time_t expires;
+ ngx_http_secure_link_ctx_t *ctx;
+ u_char hash_buf[EVP_MAX_MD_SIZE],
hmac_buf[EVP_MAX_MD_SIZE];
+ const EVP_MD *evp_md;
+ u_int hmac_len, hash_base64_len;
+
+ if (conf->variable == NULL || conf->hmac_message == NULL ||
conf->hmac_secret == NULL) {
+ goto not_found;
+ }
+
+ if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "secure link: \"%V\"", &val);
+
+ last = val.data + val.len;
+
+ p = ngx_strlchr(val.data, last, ',');
+ expires = 0;
+
+ if (p) {
+ val.len = p++ - val.data;
+
+ expires = ngx_atotm(p, last - p);
+ if (expires <= 0) {
+ goto not_found;
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);
+
+ ctx->expires.len = last - p;
+ ctx->expires.data = p;
+ }
+
+ evp_md = EVP_get_digestbyname((const char*) conf->hmac_algorithm.data);
+ if (evp_md == NULL) {
+ return NGX_ERROR;
+ }
+
+ hash.len = (u_int) EVP_MD_size(evp_md);
+ hash.data = hash_buf;
+
+ hash_base64_len = (4*hash.len+2)/3;
+ if (val.len > hash_base64_len+2) {
+ goto not_found;
+ }
+
+ if (ngx_decode_base64url(&hash, &val) != NGX_OK) {
+ goto not_found;
+ }
+
+ if (hash.len != (u_int) EVP_MD_size(evp_md)) {
+ goto not_found;
+ }
+
+ if (ngx_http_complex_value(r, conf->hmac_message, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "secure link message: \"%V\"", &val);
+
+ if (ngx_http_complex_value(r, conf->hmac_secret, &key) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ HMAC(evp_md, key.data, key.len, val.data, val.len, hmac_buf, &hmac_len);
+
+ if (ngx_memcmp(hash_buf, hmac_buf, EVP_MD_size(evp_md)) != 0) {
+ goto not_found;
+ }
+
+ v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+

static ngx_int_t
ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
@@ -306,6 +440,9 @@
* conf->variable = NULL;
* conf->md5 = NULL;
* conf->secret = { 0, NULL };
+ * conf->hmac_message = NULL;
+ * conf->hmac_secret = NULL;
+ * conf->hmac_algorithm = {0,NULL};
*/

return conf;
@@ -319,6 +456,7 @@
ngx_http_secure_link_conf_t *conf = child;

ngx_conf_merge_str_value(conf->secret, prev->secret, "");
+ ngx_conf_merge_str_value(conf->hmac_algorithm, prev->hmac_algorithm, "");

if (conf->variable == NULL) {
conf->variable = prev->variable;
@@ -328,6 +466,14 @@
conf->md5 = prev->md5;
}

+ if (conf->hmac_message == NULL) {
+ conf->hmac_message = prev->hmac_message;
+ }
+
+ if (conf->hmac_message == NULL) {
+ conf->hmac_message = prev->hmac_secret;
+ }
+
return NGX_CONF_OK;
}

_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[PATCH] Nginx secure link module with HMAC construction

timo2 4407 April 28, 2012 04:50AM

Re: [PATCH] Nginx secure link module with HMAC construction

Maxim Dounin 889 April 28, 2012 07:18AM

Re: [PATCH] Nginx secure link module with HMAC construction

timo2 1079 April 28, 2012 01:54PM

Re: [PATCH] Nginx secure link module with HMAC construction

Konstantin Baryshnikov 1239 April 29, 2012 09:04AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 179
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready