Redirect based on php-set cookies

Hi guys,

I am planning to use nginx as reserve proxy in front my Apache server.
Our site is under heavy DoS attack, where attacker sends a lot of
malformed HTTP request to flood Apache. I intend to filter out the
attacker by:

  • Setting up nginx as reserve proxy

  • If user connect for the first time, redirect to a test.php page, where
    a captcha is used to detect if it is really human.

  • If pass, a secret cookie is set to identify this user for a period of
    time. With this cookie, user will be able to go further into Apache.

As a result, I need to have some kind of rewrite rule based on checking
cookie value (this is calculated by sha1 REMOTE_ADDR + REQUEST_DATE, for
example, for each user). Could anyone suggest a easy way of implementing
this with nginx?

Thanks!

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,55378#msg-55378

Hi,

I am writing a module to do the above-mentioned task. I need to access
system date, or the date of the request (for example
$_SERVER[‘REQUEST_TIME’] in PHP) to put into security session; but I
could not find this variable in Nginx 7.x. Exists such variable in
Nginx, or I have to get the value from external library?

Thanks in advance!

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,56264#msg-56264

To use the new version of secret_cookie module

In the nginx’s config, you can use the following directives:

  • secret_cookie [on/off] : Turn on or turn off the module functionality

Note: If the module is turned off, all cookie check will return true.

  • secret_cookie_def on/off name=NAME rule=RULE duration=DURATION
    salt=SALT ua_limit=UA_LIMIT log=on/off

This directive define one secret_cookie check (you can define more than
one as required). You can set individual secret_cookie check to on or
off separately. Other settings are:

  • NAME: Name of the cookie of interest.

  • RULE: If a cookie with such a name existed in HTTP header,
    secret_cookie module will check it again this rule. The rule is: t =
    time to live, s=salt value, u=user agent, a=remote IP address. For
    example, rule=usat will check if the content of the cookie of interest
    is the sha value of user agent + salt value + remote IP address + time
    to live. You can repeat the rule if required (for example, rule=susast
    will add salt repeatedly for several place)

  • DURATION: The time for the cookie to live. If the cookie is valid for
    1 hour, then duration=3600. The duration check is only meaningful if
    rule contains “time to live”.

  • SALT: A random string to increase the secure of secret cookie. Only
    meaningful if the rule contains salt.

  • UA_LIMIT: Sometime the user agent can be very long, which will cause
    problem for secret_cookie check (more processing time, more memory etc).
    You can limit the check to only first few byte of the user agent. For
    example, ua_limit=25 means checking only first 25 bytes.

  • log=on/off: Turn on or off the logging of secret_cookie checking.

One example config with secret_cookie module enable:

# Turn secret_cookie module on
secret_cookie      on;
# Duration: 1 day = 86400, 2 day = 172800, 3 day = 259200, 1 weeks =
604800
# Define AntiDoS cookie
secret_cookie_def  on  name=AntiDoS rule=sutas duration=86400
salt=2j3ns3a ua_limit=10 log=off;
# Define RestrictedArea cooke
secret_cookie_def  on  name=RestrictedArea rule=tsau duration=7200
salt=3nh3323 us_limit=20 log=on;

# If AntiDoS cookie is not set, redirect to verification page
if ($secret_cookie_value !~ (AntiDoS)) {
     rewrite ^(.*)$ /verification/index.php;
}

location ~* /verification/.*\.php$ {
   # This is the verification location, where you should setup a php
captcha for user/bot identification.
   # If captcha is valid, assign user an AntiDoS cookie which match the
above rule.
}

# These are protected areas, only Admin can enter
location ~* ^/(admin|forum/admincp)/ {
    # If RestrictedArea cookie is not set, then redirect to admin
verification page
    if ($secret_cookie_value !~ (RestrictedArea)){
         rewrite ^(.*)$ /admin_verification/index.php last;
    }
}

location ~* /admin_verification/.*\.php$ {
   # Again, this is verification location, where you should setup a php
captcha for admin identification.
   # If captcha is valid, assign admin an RestrictedArea cookie which
match the above rule.
}

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,173804#msg-173804

Not sure if anyone interested in this module, but here is the code I
made:

/*

  • Author: tqvn2004
  • Date: 24/02/2010
    */

#include
#include
#include
#include

/*

  • The module set the $secret_cookie variable
  • to “true” if a secret cookie is set at client.
  • Based on the variable, user can be classified
  • into verified and non-verified one.
    */

#define NGX_HTTP_SECRET_COOKIE_OFF 0 // Default state
#define NGX_HTTP_SECRET_COOKIE_ON 1

#define NGX_HTTP_SECRET_COOKIE_NOT_SET 1
#define NGX_HTTP_SECRET_COOKIE_SET 0

#define NGX_HTTP_SECRET_COOKIE_DEFAULT_VALID_PERIOD 604800 // 7 days
#define NGX_HTTP_SECRET_COOKIE_MIN_VALID_PERIOD 3600 // 1 hour

// Default rule: Salt + Remote-address + User-agent + Time + Salt
#define NGX_HTTP_SECRET_COOKIE_DEFAULT_RULE “sauts”
#define NGX_HTTP_SECRET_COOKIE_DEFAULT_SALT “x83n7h32”
#define NGX_HTTP_SECRET_COOKIE_DEFAULT_NAME “secret_cookie”

// Record for Location configuration
typedef struct {
ngx_flag_t enable;
ngx_str_t name;
ngx_str_t rule;
ngx_str_t salt;
ngx_uint_t validperiod;

} ngx_http_secret_cookie_conf_t;

// Return variable structure
typedef struct {
ngx_str_t name;
ngx_http_get_variable_pt handler;
uintptr_t data;
} ngx_http_secret_cookie_variable_t;

// Context variable: This alive for request’s duration
typedef struct {
ngx_uint_t secret_cookie_status;
ngx_str_t secret_cookie;
} ngx_http_secret_cookie_ctx_t;

static void *ngx_http_secret_cookie_create_conf(ngx_conf_t *cf);
static char *ngx_http_secret_cookie_merge_conf(ngx_conf_t *cf, void
*parent,
void *child);
static ngx_int_t ngx_http_secret_cookie_add_variable(ngx_conf_t *cf);
static ngx_int_t ngx_http_secret_cookie_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_http_secret_cookie_ctx_t *
ngx_http_secret_cookie(ngx_http_request_t *r,
ngx_http_secret_cookie_conf_t *cf);
static char * ngx_http_secret_cookie_valid_period(ngx_conf_t *cf, void
*post,
void *data);
static char * ngx_http_secret_cookie_name(ngx_conf_t *cf, void *post,
void *data);
static char * ngx_http_secret_cookie_rule(ngx_conf_t *cf, void *post,
void *data);
static u_char * ngx_num2str(u_char *buf, u_char *last, uint64_t ui64);
static ngx_int_t ngx_raw_vs_hex(u_char *rawbuf, size_t rlen, u_char
*hexbuf, size_t hlen);

// A pointer to post-handler function to validate the “validperiod”
param
static ngx_conf_post_handler_pt ngx_http_secret_cookie_valid_period_p =
ngx_http_secret_cookie_valid_period;
static ngx_conf_post_handler_pt ngx_http_secret_cookie_name_p =
ngx_http_secret_cookie_name;
static ngx_conf_post_handler_pt ngx_http_secret_cookie_rule_p =
ngx_http_secret_cookie_rule;

static ngx_command_t ngx_http_secret_cookie_commands[] = {
/* Define secret_cookie directive (on or off).
* Location: Main, HTTP, Location
* Take: 1 parameter, a Flag
*/
{ ngx_string(“secret_cookie”),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot, // Convert on/off to value
NGX_HTTP_LOC_CONF_OFFSET, // Instruct value to be written to
Location Config,
offsetof(ngx_http_secret_cookie_conf_t, enable), // with this
offset
NULL }, // No post-handler is necessary.

/* Define secret_cookie_var1 directive
 * Location: Main, HTTP, Location
 * Taking: 1 parameter, a string
 */
{ ngx_string("secret_cookie_rule"),
  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_secret_cookie_conf_t, rule),
  &ngx_http_secret_cookie_rule_p },

{ ngx_string("secret_cookie_salt"),
  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_secret_cookie_conf_t, salt),
  NULL },

{ ngx_string("secret_cookie_name"),
  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_secret_cookie_conf_t, name),
  &ngx_http_secret_cookie_name_p },

{ ngx_string("secret_cookie_valid_period"),
  NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  ngx_conf_set_num_slot,
  NGX_HTTP_LOC_CONF_OFFSET,
  offsetof(ngx_http_secret_cookie_conf_t, validperiod),
  &ngx_http_secret_cookie_valid_period_p }, // Post-handler function

  ngx_null_command

};

static ngx_http_module_t ngx_http_secret_cookie_module_ctx = {
ngx_http_secret_cookie_add_variable, /* preconfiguration /
NULL, /
postconfiguration */

NULL,                                 /* create main configuration 

/
NULL, /
init main configuration */

NULL,                                 /* create server configuration 

/
NULL, /
merge server configuration
*/

ngx_http_secret_cookie_create_conf,   /* create location 

configuration /
ngx_http_secret_cookie_merge_conf /
merge location
configuration */
};

ngx_module_t ngx_http_secret_cookie_module = {
NGX_MODULE_V1,
&ngx_http_secret_cookie_module_ctx, /* module context /
ngx_http_secret_cookie_commands, /
module directives /
NGX_HTTP_MODULE, /
module type /
NULL, /
init master /
NULL, /
init module /
NULL, /
init process /
NULL, /
init thread /
NULL, /
exit thread /
NULL, /
exit process /
NULL, /
exit master */
NGX_MODULE_V1_PADDING
};

static ngx_http_secret_cookie_variable_t ngx_http_secret_cookie_vars[]
= {
{ ngx_string(“secret_cookie_not_set”),
ngx_http_secret_cookie_variable,
NGX_HTTP_SECRET_COOKIE_NOT_SET },
{ ngx_null_string, NULL, 0 }
};

/*

  • This function set the $secret_cookie to 1, if the

  • cookie is set and valid.
    */
    static ngx_int_t
    ngx_http_secret_cookie_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v,
    uintptr_t data)
    {
    ngx_http_secret_cookie_ctx_t *rctx;
    ngx_http_secret_cookie_conf_t *cf;

    cf = ngx_http_get_module_loc_conf(r, ngx_http_secret_cookie_module);
    // Only perform cookie search if the module is enabled
    if (cf->enable){
    rctx = ngx_http_secret_cookie(r, cf);

     if ((rctx != NULL) &&
         (rctx->secret_cookie_status == NGX_HTTP_SECRET_COOKIE_SET)) 
    

{
*v = ngx_http_variable_null_value;
return NGX_OK;
}
}
// Always return true (i.e. secret_cookie not set),
// unless module is enalbed and secret_cookie is checked…
*v = ngx_http_variable_true_value;
return NGX_OK;
}

/*

  • This is the main function: It looks for a secret cookie

  • in the HTTP request header, and compared with constructed value

  • Return: Secret Cookie SET or NOT SET!
    */
    static ngx_http_secret_cookie_ctx_t *
    ngx_http_secret_cookie(ngx_http_request_t *r,
    ngx_http_secret_cookie_conf_t *cf)
    {
    ngx_int_t n, alen, tlen, slen, ulen, total_len;
    ngx_http_secret_cookie_ctx_t *ctx;
    time_t timestamp; // Current UNIX timestamp
    u_char *user_agent = NULL, *salt = NULL,
    *remote_addr = NULL, *last_change = NULL;
    u_char *secret_cookie;
    u_char *p;
    ngx_sha1_t sha_ctx;
    #if (NGX_DEBUG)
    // A string for print debug info
    ngx_str_t mypen;

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “Start checking the secret_cookie:”);
    #endif
    ctx = ngx_http_get_module_ctx(r, ngx_http_secret_cookie_module);

    // If existed, secret_cookie was checked before, so stop!
    if (ctx){
    #if (NGX_DEBUG)
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “The secret_cookie was checked successfully before,
    aborting!”);
    #endif
    return ctx;
    }
    // Otherwise, start the checking process
    if (ctx == NULL) {
    #if (NGX_DEBUG)
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “Context variable for secret_cookie not available,
    creating one!”);
    #endif
    // This r->pool temporary memory will be destroyed later,
    // after this request is served!
    ctx = ngx_pcalloc(r->pool,
    sizeof(ngx_http_secret_cookie_ctx_t));
    if (ctx == NULL) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: Out of memory”);
    return NULL;
    }
    ngx_http_set_ctx(r, ctx, ngx_http_secret_cookie_module);
    }

    ctx->secret_cookie_status = NGX_HTTP_SECRET_COOKIE_NOT_SET;

    // This is copied from USERID Module:
    n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies,
    &cf->name,
    &ctx->secret_cookie);
    if (n == NGX_DECLINED) {
    #if (NGX_DEBUG)
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “The secret_cookie not found in the header!”);
    #endif
    return ctx;
    }
    #if (NGX_DEBUG)
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “Got a secret_cookie: “%V” - “%V””, &cf->name,
    &ctx->secret_cookie);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “Rule to compute the secret_cookie: “%V””,
    &cf->rule);
    #endif
    // First, prepare the variables and count the required length
    n = 0;
    total_len = 0;
    alen = -1;
    ulen = -1;
    tlen = -1;
    slen = -1;
    while (n < (ngx_int_t) cf->rule.len){
    switch (cf->rule.data){
    case ‘a’:
    if (alen == -1){
    // Prepare “remote-addr” buffer and its length
    alen = r->connection->addr_text.len;
    remote_addr = r->connection->addr_text.data;
    #if (NGX_DEBUG)
    mypen.len = alen;
    mypen.data = remote_addr;
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP,
    r->connection->log, 0,
    “Remote address to compute the secret_cookie:
    “%i” - “%V””, alen, &mypen);
    #endif
    }
    total_len += alen;
    break;
    case ‘t’:
    if (tlen == -1){
    // Prepare “last_change” buffer and its length
    // Obtain current time in second
    timestamp = ngx_time();
    // Calculate the last_change and convert it to
    string
    last_change = ngx_pcalloc(r->pool, NGX_INT64_LEN);
    if (last_change == NULL) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log,
    0,
    “secret_cookie: Out of memory”);
    return ctx;
    }
    p = ngx_num2str(last_change,
    last_change + NGX_INT64_LEN,
    (uint64_t) timestamp /
    cf->validperiod);
    tlen = p - last_change;
    #if (NGX_DEBUG)
    mypen.len = tlen;
    mypen.data = last_change;
    ngx_log_debug4(NGX_LOG_DEBUG_HTTP,
    r->connection->log, 0,
    “Last change (time) to compute the
    secret_cookie: “%i” / “%i” = “%V” (”%i")",
    timestamp, cf->validperiod, &mypen, tlen);
    #endif
    }
    total_len += tlen;
    break;
    case ‘s’:
    if (slen == -1){
    // Prepare “salt” buffer and its length
    slen = cf->salt.len;
    salt = cf->salt.data;
    #if (NGX_DEBUG)
    mypen.len = slen;
    mypen.data = salt;
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
    r->connection->log, 0,
    “Salt to compute the secret_cookie: “%i” -
    “%V””, slen, &mypen);
    #endif
    }
    total_len += slen;
    break;
    case ‘u’:
    if (ulen == -1){
    // Prepare “user-agent” buffer and its length
    ulen = r->headers_in.user_agent->value.len;
    user_agent = r->headers_in.user_agent->value.data;
    #if (NGX_DEBUG)
    mypen.len = ulen;
    mypen.data = user_agent;
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
    r->connection->log, 0,
    "User agent to compute the secret_cookie: “%i”

  • “%V”", ulen, &mypen);
    #endif
    }
    total_len += ulen;
    default:
    // Do not copy char
    break;
    }
    n++;
    }

    // Second, create the content of secret_cookie
    secret_cookie = ngx_pcalloc(r->pool, total_len);
    if (secret_cookie == NULL) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: Out of memory”);
    return ctx;
    }
    n = 0;
    p = secret_cookie;
    while (n < (ngx_int_t) cf->rule.len){
    switch (cf->rule.data){
    case ‘a’:
    // This ngx_copy faster than ngx_cpymem with buffer <
    16 bytes
    p = ngx_copy(p, remote_addr, alen);
    break;
    case ‘t’:
    p = ngx_copy(p, last_change, tlen);
    break;
    case ‘s’:
    p = ngx_copy(p, salt, slen);
    break;
    case ‘u’:
    p = ngx_cpymem(p, user_agent, ulen);
    break;
    }
    n++;
    }
    #if (NGX_DEBUG)
    mypen.len = total_len;
    mypen.data = secret_cookie;
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “The content of the secret_cookie: “%i” “%V””,
    total_len, &mypen);
    #endif
    // Third, perform sha1 on the content
    p = ngx_pcalloc(r->pool, SHA_DIGEST_LENGTH);
    if (p == NULL) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: Out of memory”);
    return ctx;
    }
    n = ngx_sha1_init(&sha_ctx);
    if (n == 0) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: SHA1 init fails”);
    return ctx;
    }
    n = ngx_sha1_update(&sha_ctx, secret_cookie, total_len);
    if (n == 0) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: SHA1 update fails”);
    return ctx;
    }
    n = ngx_sha1_final(p, &sha_ctx);
    if (n == 0) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: SHA1 final fails”);
    return ctx;
    }
    #if (NGX_DEBUG)
    mypen.len = SHA_DIGEST_LENGTH;
    mypen.data = p;
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    “The content of the secret_cookie after sha1: “%i”
    “%V””, SHA_DIGEST_LENGTH, &mypen);
    #endif
    if (ngx_raw_vs_hex(p, SHA_DIGEST_LENGTH, ctx->secret_cookie.data,
    2*SHA_DIGEST_LENGTH) == 0){
    ctx->secret_cookie_status = NGX_HTTP_SECRET_COOKIE_SET;
    } else {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
    “secret_cookie: Saved cookie does not match”);
    }

    // Return the result
    return ctx;
    }

/* Add $secret_cookie_set variable to the http process.

  • Called by “preconfiguration” hook!
    */
    static ngx_int_t
    ngx_http_secret_cookie_add_variable(ngx_conf_t *cf)
    {
    ngx_http_secret_cookie_variable_t *var;
    ngx_http_variable_t *v;

    for (var = ngx_http_secret_cookie_vars; var->name.len; var++) {

     v = ngx_http_add_variable(cf, &var->name, 
    

NGX_HTTP_VAR_CHANGEABLE);
if (v == NULL) {
return NGX_ERROR;
}

    v->get_handler = var->handler;
    v->data = var->data;
}

return NGX_OK;

}

/*

  • This function is called when location configuration is created.

  • It will create a default configuration for secret_cookie module.
    */
    static void *
    ngx_http_secret_cookie_create_conf(ngx_conf_t *cf)
    {
    ngx_http_secret_cookie_conf_t *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secret_cookie_conf_t));
    if (conf == NULL) {
    return NULL;
    }

    /*

    • Set default value
      /
      conf->enable = NGX_CONF_UNSET;
      /
      This should be set by ngx_pcalloc():
      conf->rule = ngx_null_string;
      conf->salt = ngx_null_string;
      conf->name = ngx_null_string; */
      conf->validperiod = NGX_CONF_UNSET;
      return conf;
      }

/*

  • This function is called when location configuration is merged to main
    config.

  • It will create a default configuration for secret_cookie module.
    */
    static char *
    ngx_http_secret_cookie_merge_conf(ngx_conf_t *cf, void *parent, void
    *child)
    {
    ngx_http_secret_cookie_conf_t *prev = parent;
    ngx_http_secret_cookie_conf_t *conf = child;

    // Merge previous to current configration, with a default value:
    ngx_conf_merge_value(conf->enable, prev->enable,
    NGX_HTTP_SECRET_COOKIE_OFF);
    ngx_conf_merge_str_value(conf->rule, prev->rule,
    NGX_HTTP_SECRET_COOKIE_DEFAULT_RULE);
    ngx_conf_merge_str_value(conf->salt, prev->salt,
    NGX_HTTP_SECRET_COOKIE_DEFAULT_SALT);
    ngx_conf_merge_str_value(conf->name, prev->name,
    NGX_HTTP_SECRET_COOKIE_DEFAULT_NAME);
    ngx_conf_merge_uint_value(conf->validperiod, prev->validperiod,
    NGX_HTTP_SECRET_COOKIE_DEFAULT_VALID_PERIOD);

    if (conf->validperiod < NGX_HTTP_SECRET_COOKIE_MIN_VALID_PERIOD) {
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    “Valid period is too short!”);
    return NGX_CONF_ERROR;
    }
    return NGX_CONF_OK;
    }

/*

  • This function is called after secret_cookie_valid_period is passed

  • from config to module. Here you can validate the param, or change the

  • param to meaningful value.
    */
    static char *
    ngx_http_secret_cookie_valid_period(ngx_conf_t *cf, void *post, void
    *data)
    {
    ngx_uint_t *validperiod = data;

    if (*validperiod < NGX_HTTP_SECRET_COOKIE_MIN_VALID_PERIOD) {
    return “Valid period is too short (minimum 3600 second or 1
    hour)”;
    }

    return NGX_CONF_OK;
    }

/*

  • This function is called after secret_cookie_name is passed from
    config

  • to module. Here you can validate the param, or change the

  • param to meaningful value.
    */
    static char *
    ngx_http_secret_cookie_name(ngx_conf_t *cf, void *post, void *data)
    {
    ngx_str_t *name = data;

    if (ngx_strcmp(name->data, “”) == 0) {
    name->len = sizeof(NGX_HTTP_SECRET_COOKIE_DEFAULT_NAME) - 1;
    name->data = (u_char *) NGX_HTTP_SECRET_COOKIE_DEFAULT_NAME;
    }

    return NGX_CONF_OK;
    }

/*

  • This function is called after secret_cookie_rule is passed from
    config

  • to module. Here you can validate the param, or change the

  • param to meaningful value.
    */
    static char *
    ngx_http_secret_cookie_rule(ngx_conf_t *cf, void *post, void *data)
    {
    ngx_str_t *rule = data;
    u_char *new;
    size_t i, len;

    // First count the required length,
    i = 0;
    len = 0;
    while (i < rule->len){
    switch (ngx_tolower(rule->data)){
    case ‘a’:
    case ‘t’:
    case ‘s’:
    case ‘u’:
    len++;
    break;
    default:
    // Do not copy char
    break;
    }
    i++;
    }

    if (len == 0) {
    return “must contain combination of “s” (Salt) and “a”
    (Remote-address) and “u” (User-agent) and “t” (Time)”;
    }

    // Then do the copying
    new = ngx_pnalloc(cf->pool, len);
    i = 0;
    len = 0;
    while (i < rule->len){
    switch (ngx_tolower(rule->data)){
    case ‘a’:
    case ‘t’:
    case ‘s’:
    case ‘u’:
    new = ngx_tolower(rule->data);
    len++;
    break;
    default:
    // Do not copy char
    break;
    }
    i++;
    }

    rule->len = len;
    rule->data = new;

    return NGX_CONF_OK;
    }
    /*

  • Convert an int64 to string presentation
    */
    static u_char * ngx_num2str(u_char *buf, u_char *last, uint64_t ui64)
    {
    u_char p, temp;
    /

    * we need temp only,
    * but icc issues the warning
    */
    size_t len;
    uint32_t ui32;

    p = temp + NGX_INT64_LEN;

    if (ui64 <= NGX_MAX_UINT32_VALUE) {
    ui32 = (uint32_t) ui64;

     do {
         *--p = (u_char) (ui32 % 10 + '0');
     } while (ui32 /= 10);
    

    } else {
    do {
    *–p = (u_char) (ui64 % 10 + ‘0’);
    } while (ui64 /= 10);
    }

    /* number safe copy */

    len = (temp + NGX_INT64_LEN) - p;

    if (buf + len > last) {
    len = last - buf;
    }

    return ngx_cpymem(buf, p, len);
    }

/*

  • Comparison of a raw string and a hex-style string
    */
    static ngx_int_t ngx_raw_vs_hex(u_char rawbuf, size_t rlen, u_char
    hexbuf, size_t hlen)
    {
    static u_char hex[] = “0123456789abcdef”;
    u_char hexchar[] = “aa”;
    ngx_int_t len, i;
    // Compare by the shortest length
    len = ((rlen < hlen / 2) ? rlen : hlen / 2);
    i = 0;
    while (i < len){
    // Calculate a hexchar from rawbuff
    hexchar[1] = hex & 0xf];
    hexchar[0] = hex[(rawbuf>>4) & 0xf];
    if ((hexchar[0] != hexbuf[2
    i]) || (hexchar[1] !=
    hexbuf[2
    i+1])){
    return 1;
    }
    i++;
    }
    return 0;
    }

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,56822#msg-56822

I Became an error can i contact you ?

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,219859#msg-219859

Thank to nginx, our server was able to mitigate the Slowloris DoS
attack, which killed Apache with partial or malformed HTTP headers. The
above mentioned secret_cookie module helped to filter out all other
valid botnet requests with this config:

[code]## If secret_cookie not set, redirect to verification page
if ($secret_cookie_not_set) {
#store the old uri
set $uri_old $uri$is_args$args;
rewrite ^(.*)$ /verification/index.php;
}

Verification page: a php code that check for user response (for

example recaptcha),

and setting the secret_cookie if response is valid

location ~* /verification/.*.php$ {
# Do not allow direct access
internal;
# If IP in the white list,
# return without the checking process
if ($whitelist ~ ^bot) {
rewrite ^ $uri_old last;
}
# Turn off the proxy’s cache
proxy_cache off;

# Turn off access log, but turn on error log
access_log off;
error_log /usr/local/nginx-proxy3/logs/verification.log error;

# Set the rate limite for verification page
limit_req zone=vzone burst=15 nodelay;
# Also set the connection limit for verification page
limit_conn gulag 5;
include /usr/local/nginx-proxy3/conf/fastcgi.conf;
fastcgi_pass  127.0.0.1:9002;
fastcgi_index index.php;
fastcgi_param  SCRIPT_FILENAME

/home/tqvn2004/public_html$fastcgi_script_name;
fastcgi_param URI_OLD $uri_old;
}[/code]

And here is an example index.php code for user verification:

[code]<?php
$x = rand(1,15);
$y = rand(1,15);

Secret cookie setting, must match one define in nginx.conf

$secret_cookie_salt = “aDt%&sa”;
$secret_cookie_valid_period = 86400;
$secret_cookie_name = “secret_cookie”;
$errormsg = “”;
$result_cookie_name = “verify_human”;
$uri_cookie_name = “reference_uri”;
$verification_uri = ‘/verification/index.php’;
$test_uri = ‘/verification/’;
$domain = “.example.org”;
setcookie($result_cookie_name,
sha1($secret_cookie_salt . strval($x+$y) .
$secret_cookie_salt),
time() + 600,
‘/verification’, // Only available in ‘verification’ folder
$domain, FALSE, TRUE);
if (isset($_SERVER[‘URI_OLD’]) && (strpos($_SERVER[‘URI_OLD’],
$test_uri) !== 0)){
setcookie($uri_cookie_name,
$_SERVER[‘URI_OLD’],
time() + 600,
‘/verification’, // Only available in 'verification
folder
$domain, FALSE, TRUE);
}

if (isset($_GET) && isset($_GET[‘result’])) {
if (isset($_COOKIE) && isset($_COOKIE[$result_cookie_name])){
if (sha1($secret_cookie_salt . $_GET[‘result’] .
$secret_cookie_salt)
== $_COOKIE[$result_cookie_name]){
// Set the secret cookie and redirect
setcookie($secret_cookie_name,
sha1($secret_cookie_salt .

floor($_SERVER[‘REQUEST_TIME’]/$secret_cookie_valid_period) .
$_SERVER[‘REMOTE_ADDR’] .
$_SERVER[‘HTTP_USER_AGENT’] .
$secret_cookie_salt),
time()+$secret_cookie_valid_period,
‘/’,
$domain, FALSE, TRUE);
//$http = (isset($_SERVER[‘HTTPS’]) ? ‘https://’ :
‘http://’);
//$port = ($_SERVER[‘SERVER_PORT’] == 80 ? ‘’ :
‘:’.$_SERVER[‘SERVER_PORT’]);
$http = ($_SERVER[‘SERVER_PORT’] == 443 ? ‘https://’ :
‘http://’);
$uri = (!isset($_COOKIE[$uri_cookie_name]) ? ‘/’ :
$_COOKIE[$uri_cookie_name]);
// Redirect to new location
header("Location: " . $http . $_SERVER[‘HTTP_HOST’] .
$uri);
} else {
$errormsg = “The result is not correct, try again!!!”;

    }

} else {
$errormsg = “Cannot verify your result. Try again!! You should
also check if: (1) your browser does accept cookies, (2) your proxy, if
any, does accept cookies…”;
}
}

echo ’

body {
background-color: #999999;
font-size:13px;
font-family: Verdana, Arial, Helvetica, sans-serif;
}
div#outer {
width: 60%;
background-color:#FFFFFF;
margin-top: 50px;
margin-bottom: 50px;
margin-left: auto;
margin-right: auto;
padding: 0px;
border: thin solid #000000;
}
div#header {
padding: 15px;
margin: 0px;
text-align: center;
color: #999999;
}
div#main {
margin-top: 0px;
margin-bottom: 20px;
padding: 10px;
text-align: center;
font-size: 15px;
}
div#main_header {
padding-bottom: 15px;
}
div#main_error {
padding-top: 15px;
color: red;
}
#formula{
font-weight:bold;
font-size: 20px;
}
div#footer {
padding: 15px;
margin: 0px;
border-top: thin solid #000000;
text-align: center;
}

Example.org

    Example.org


  Perform the math question:' .
  '
      '. $x .' + '. $y .' =
  '.$errormsg.'
  ' .
'

    Our server is under heavy DoS attack, this verification page

helps us identify you as human.

';
?>
[/code]

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,132704#msg-132704

For someone who is interested in using this module

To compile with nginx, do the following steps:

  1. Download Nginx source code from http://wiki.nginx.org/NginxInstall,
    and put them in, let’s say, folder ‘nginx’

  2. Install OpenSSL for your Linux.

  3. You need also to download OpenSSL source code, and copy folder
    openssl/crypto/sha to nginx/src. This step is important, as the
    secret_cookie module depends on OpenSSL at compile time.

  4. Copy sha/sha.h in openssl/crypto/sha into nginx/src/core. You will
    get compilation error if not doing this.

  5. Now create a folder name nginx/src/secret_cookie to store the
    secret_cookie module. Put the config and ngx_http_secret_cookie_module.c
    (described below) in this folder.

  6. Go to folder nginx and now you can configure the make:

./configure --prefix=/usr/local/nginx-proxy/ --with-http_ssl_module
–add-module=src/secret_cookie --with-sha1=src/sha

Please change the directory as per your server configuration.

  1. Run make, if everything is ok, you should now be able to compile
    nginx with the secret_cookie module.

Content of the config file:

ngx_addon_name=ngx_http_secret_cookie_module HTTP_MODULES="$HTTP_MODULES ngx_http_secret_cookie_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_secret_cookie_module.c"

Content of the ngx_http_secret_cookie_module.c file:

/*
 * Copyright (C) Huan Cong Nguyen
 * Version 1.0.6
 */

#include
#include
#include
#include

/*
 * This module set the  $secret_cookie_value variable
 * to the names of valid secret cookies set at client.
 * Based on the variable, user can be classified
 * into verified and non-verified one.
 */
#define NGX_HTTP_SECRET_COOKIE_OFF                  0 // Default state
#define NGX_HTTP_SECRET_COOKIE_ON                   1

#define NGX_HTTP_SECRET_COOKIE_NOT_SET              0

#define NGX_HTTP_SECRET_COOKIE_MINIMUM_DURATION     600    // 10
minutes
#define NGX_HTTP_SECRET_COOKIE_MINIMUM_UA_LIMIT     10     // 10
characters
#define NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE   12     // Allow only
12 characters in name, salt and rule

// Rule elements
#define NGX_HTTP_SECRET_COOKIE_RULE_SALT            's'
#define NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS         'a'
#define NGX_HTTP_SECRET_COOKIE_RULE_TIME            't'
#define NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT      'u'
#define NGX_HTTP_SECRET_COOKIE_DIVIDER              ' '

// Record for each secret cookie
typedef struct {
    ngx_flag_t                  status;
    ngx_flag_t                  log;
    ngx_uint_t                  duration;
    ngx_str_t                   name;
    ngx_str_t                   rule;
    ngx_str_t                   salt;
    ngx_uint_t                  ua_limit;
} ngx_http_secret_cookie_t;

// Record for Location configuration
typedef struct {
    ngx_flag_t                  status;
    ngx_array_t                 *secret_cookies;
    ngx_http_variable_value_t   *secret_cookie_value_disabled;
} ngx_http_secret_cookie_conf_t;

// Context variable: This alive for request's duration
typedef struct {
    ngx_http_variable_value_t   *secret_cookie_value;
} ngx_http_secret_cookie_ctx_t;

// Return variable structure
typedef struct {
    ngx_str_t                   name;
    ngx_http_get_variable_pt    handler;
    uintptr_t                   data;
} ngx_http_secret_cookie_variable_t;

static void *ngx_http_secret_cookie_create_conf(ngx_conf_t *cf);
static char *ngx_http_secret_cookie_merge_conf(ngx_conf_t *cf, void
*parent,
    void *child);
static ngx_int_t ngx_http_secret_cookie_add_variable(ngx_conf_t *cf);
static ngx_int_t ngx_http_secret_cookie_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
static ngx_http_secret_cookie_ctx_t *
ngx_http_secret_cookie_handler(ngx_http_request_t *r,
    ngx_http_secret_cookie_conf_t *cf);
static char *ngx_http_secret_cookie_def(ngx_conf_t *cf, ngx_command_t
*cmd, void *conf);
static u_char * ngx_num2str(u_char *buf, u_char *last, uint64_t ui64);
static ngx_int_t ngx_raw_vs_hex(u_char *rawbuf, size_t rlen, u_char
*hexbuf, size_t hlen);

static ngx_command_t  ngx_http_secret_cookie_commands[] = {
    /* Define secret_cookie directive (on or off).
     * Location: Main, HTTP
     * Take: 1 parameter, a Flag
     */
    { ngx_string("secret_cookie"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_flag_slot, // Convert on/off to value
      NGX_HTTP_LOC_CONF_OFFSET, // Instruct value to be written to
Location Config,
      offsetof(ngx_http_secret_cookie_conf_t, status), // with this
offset
      NULL }, // No post-handler is necessary.

    /* Define secret_cookie_def directive ([off] name=xyz salt=abc
rule=mnk duration=8323)
     * Location: Main, HTTP
     * Taking: 4 or 5 parameters
     */
    { ngx_string("secret_cookie_def"),

NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE4|NGX_CONF_TAKE5|NGX_CONF_TAKE6|NGX_CONF_TAKE7,
      ngx_http_secret_cookie_def,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

      ngx_null_command
};


static ngx_http_module_t  ngx_http_secret_cookie_module_ctx = {
    ngx_http_secret_cookie_add_variable,  /* preconfiguration */
    NULL,                                 /* postconfiguration */

    NULL,                                 /* create main configuration
*/
    NULL,                                 /* init main configuration */

    NULL,                                 /* create server configuration
*/
    NULL,                                 /* merge server configuration
*/

    ngx_http_secret_cookie_create_conf,   /* create location
configuration */
    ngx_http_secret_cookie_merge_conf     /* merge location
configuration */
};


ngx_module_t  ngx_http_secret_cookie_module = {
    NGX_MODULE_V1,
    &ngx_http_secret_cookie_module_ctx,    /* module context */
    ngx_http_secret_cookie_commands,       /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

static ngx_http_secret_cookie_variable_t  ngx_http_secret_cookie_vars[]
= {
    { ngx_string("secret_cookie_value"),
ngx_http_secret_cookie_variable,
            NGX_HTTP_SECRET_COOKIE_NOT_SET },
    { ngx_null_string, NULL, 0 }
};

/*
 * This function set the $secret_cookie_value to found secret_cookie
names,
 * if they are set and valid.
 */
static ngx_int_t
ngx_http_secret_cookie_variable(ngx_http_request_t *r,
                                ngx_http_variable_value_t *v,
                                uintptr_t data)
{
    ngx_http_secret_cookie_ctx_t   *rctx;
    ngx_http_secret_cookie_conf_t  *cf;

    cf = ngx_http_get_module_loc_conf(r,
ngx_http_secret_cookie_module);
    // Check if the module is enable?
    if (cf->status == NGX_HTTP_SECRET_COOKIE_ON){
        // Only perform cookie search if the module is enable
        rctx = ngx_http_secret_cookie_handler(r, cf);

        if (rctx != NULL){
#if (NGX_DEBUG)
            ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                          "secret_cookie: Rctx is returned with
value");
#endif
            *v = *rctx->secret_cookie_value;
            return NGX_OK;
        } else {
            // There is something wrong with the search for secret
cookies,
            // just return null value to fail all user-defined checks
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: Rctx is null, which is often
due to lack of memory");
            *v = ngx_http_variable_null_value;
            return NGX_OK;
        }
    }
    // In case the secret_cookie is disabled,
    // all defined secret cookies are assumed to be found,
    // so that user-defined checks are successful
    *v = *cf->secret_cookie_value_disabled;
    return NGX_OK;
}

/*
 * This is the main function: It looks for a secret cookie
 * in the HTTP request header, and compared with constructed value
 * Return: Secret Cookie SET or NOT SET!
 */
static ngx_http_secret_cookie_ctx_t *
ngx_http_secret_cookie_handler(ngx_http_request_t *r,
ngx_http_secret_cookie_conf_t *cf)
{
    ngx_http_secret_cookie_ctx_t  *ctx;
    ngx_http_secret_cookie_t      *sc;

    ngx_uint_t                    n, i, ua_len;
    ngx_int_t                     m;
    ngx_str_t                     name, rule, salt, last_change,
                                  remote_address, user_agent,
                                  computed_sc, submitted_sc, sc_string,
                                  secret_cookie_value;
    time_t                        timestamp; // Current UNIX timestamp
    u_char                        *p;
    ngx_sha1_t                    sha_ctx;

    // Try to get the context variable
    ctx = ngx_http_get_module_ctx(r, ngx_http_secret_cookie_module);

    // If context existed, secret_cookie was checked before, so stop!
    if (ctx){
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: Checking was done before,
aborting!");
#endif
        return ctx;
    }
    // Otherwise, start the checking process
    if (ctx == NULL) {
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: Checking was not done before, start
checking");
#endif
        // This r->pool temporary memory will be destroyed later,
        // after this request is served!
        ctx = ngx_pcalloc(r->pool,
sizeof(ngx_http_secret_cookie_ctx_t));
        if (ctx == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "secret_cookie: Out of memory for context variable
(ctx)");
            return NULL;
        }
        ngx_http_set_ctx(r, ctx, ngx_http_secret_cookie_module);
    }
    ctx->secret_cookie_value = ngx_palloc(r->pool,
sizeof(ngx_http_variable_value_t));
    if (ctx->secret_cookie_value == NULL){
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                "secret_cookie: Out of memory for
ctx->secret_cookie_value variable");
        return NULL;
    }
    *ctx->secret_cookie_value = ngx_http_variable_null_value;

    remote_address.len   = r->connection->addr_text.len;
    remote_address.data  = r->connection->addr_text.data;
    if (r->headers_in.user_agent == NULL) {
        // The user-agent value is null
        user_agent.len       = 0;
        user_agent.data      = NULL;
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: User agent is empty");
#endif
    } else {
        // The user-agent value is not null
        user_agent.len       = r->headers_in.user_agent->value.len;
        user_agent.data      = r->headers_in.user_agent->value.data;
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: User agent is not empty");
#endif
    }
    secret_cookie_value.len = 0;
    // Allocate maximum size for this variable
    secret_cookie_value.data = ngx_pcalloc(r->pool,
cf->secret_cookie_value_disabled->len);
    if (secret_cookie_value.data == NULL){
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                     "secret_cookie: Out of memory for
secret_cookie_value string");
        return NULL;
    }

    sc = cf->secret_cookies->elts;
    for (i=0; i < cf->secret_cookies->nelts; i++){
        // Cookie name must be initialized first, in case of jumping to
secret_cookie_found!
        name.len   = sc[i].name.len;
        name.data  = sc[i].name.data;

        if (sc[i].status == NGX_HTTP_SECRET_COOKIE_OFF){
#if (NGX_DEBUG)
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: checking of cookie \"%V\" is off",
&name);
#endif
            goto secret_cookie_found;
        }

        // First, search for the secret_cookie's name in http header:
        m = ngx_http_parse_multi_header_lines(&r->headers_in.cookies,
&name,
                                          &submitted_sc);
        if (m == NGX_DECLINED) {
#if (NGX_DEBUG)
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: cookie \"%V\" is not found in
header", &name);
#endif
            goto secret_cookie_not_found;
        }

#if (NGX_DEBUG)
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: cookie \"%V\" found in header,
value=%V and length=%i",
                   &name, &submitted_sc, submitted_sc.len);
#endif

        if (submitted_sc.len != 2*SHA_DIGEST_LENGTH) {
#if (NGX_DEBUG)
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: size of cookie \"%V\" is incorrect",
&name);
#endif
            goto secret_cookie_not_found;
        }

        // User Agent can be very large. Limit to a certain size
        // to keep small memory footprint
        if ((sc[i].ua_limit > 0) && (user_agent.len > sc[i].ua_limit))
{
            ua_len  = sc[i].ua_limit;
        } else {
            ua_len = user_agent.len;
        }

        salt.len   = sc[i].salt.len;
        salt.data  = sc[i].salt.data;
        rule.len   = sc[i].rule.len;
        rule.data  = sc[i].rule.data;
        // Obtain current time in second
        timestamp = ngx_time();
        // Calculate the last_change and convert it to string
        last_change.data = ngx_pcalloc(r->pool, NGX_INT64_LEN);
        if (last_change.data == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                             "secret_cookie: Out of memory for
last_change string");
            return NULL;
        }
        p = ngx_num2str(last_change.data,
                        last_change.data + NGX_INT64_LEN,
                        (uint64_t) timestamp / sc[i].duration);
        last_change.len = p - last_change.data;

#if (NGX_DEBUG)
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: name has length=%i and value=%V",
name.len, &name);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: salt has length=%i and value=%V",
salt.len, &salt);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: rule has length=%i and value=%V",
rule.len, &rule);
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: duration = %i", sc[i].duration);
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: last_change = %V", &last_change);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: remote_address has length=%i and
value=%V",
                   remote_address.len, &remote_address);
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: user-agent has length=%i from
value=%V",
                   ua_len, &user_agent);
#endif

        // Second, compute the secret_cookie according to the rule:
        sc_string.len = 0;
        for (n=0; n < rule.len; n++){
            switch (ngx_tolower(rule.data[n])){
                case NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS:
                    sc_string.len += remote_address.len;
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_SALT:
                    sc_string.len += salt.len;
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT:
                    sc_string.len += ua_len;
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_TIME:
                    sc_string.len += last_change.len;
                    break;
                default:
#if (NGX_DEBUG)
                    ngx_log_debug(NGX_LOG_DEBUG_HTTP,
r->connection->log, 0,
                       "secret_cookie: Unknown rule");
#endif
                    break;
            } // End of switch
        }

        sc_string.data = ngx_pcalloc(r->pool, sc_string.len);
        if (sc_string.data == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: Out of memory for sc_string
variable");
            return NULL;
        }
        p = sc_string.data;
        for (n=0; n < rule.len; n++){
            switch (ngx_tolower(rule.data[n])){
                case NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS:
                    // This ngx_copy faster than ngx_cpymem  with buffer
< 16 bytes
                    p = ngx_copy(p, remote_address.data,
remote_address.len);
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_SALT:
                    p = ngx_copy(p, salt.data, salt.len);
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT:
                    if (ua_len > 0){
                        p = ngx_cpymem(p, user_agent.data, ua_len);
                    }
                    break;
                case NGX_HTTP_SECRET_COOKIE_RULE_TIME:
                    p = ngx_copy(p, last_change.data, last_change.len);
                    break;
                default:
#if (NGX_DEBUG)
                    ngx_log_debug(NGX_LOG_DEBUG_HTTP,
r->connection->log, 0,
                       "secret_cookie: Unknown rule");
#endif
                    break;
            } // End of switch
        }
#if (NGX_DEBUG)
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: String to compute secret_cookie has
length=%i and value=%V",
                   sc_string.len, &sc_string);
#endif

        // Third, perform sha1 on the content
        computed_sc.len   =  SHA_DIGEST_LENGTH;
        computed_sc.data  =  ngx_pcalloc(r->pool, SHA_DIGEST_LENGTH);
        if (computed_sc.data == NULL) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: Out of memory for computed_sc
variable");
            return NULL;
        }
        n = ngx_sha1_init(&sha_ctx);
        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: SHA1 init fails");
            return NULL;
        }
        n = ngx_sha1_update(&sha_ctx, sc_string.data, sc_string.len);
        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: SHA1 update fails");
            return NULL;
        }
        n = ngx_sha1_final(computed_sc.data, &sha_ctx);
        if (n == 0) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "secret_cookie: SHA1 final fails");
            return NULL;
        }
        // Release the sc_string memory:

#if (NGX_DEBUG)
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "secret_cookie: computed_secret_cookie has length=%i
and value=%V", SHA_DIGEST_LENGTH, &computed_sc);
#endif
        if (ngx_raw_vs_hex(computed_sc.data, SHA_DIGEST_LENGTH,
submitted_sc.data, 2*SHA_DIGEST_LENGTH) == 0){
            if (sc[i].log == NGX_HTTP_SECRET_COOKIE_ON){
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "secret_cookie: cookie \"%V\" is valid", &name);
            }
            goto secret_cookie_found;
        }
#if (NGX_DEBUG)
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                      "secret_cookie: cookie \"%V\" does not match",
&name);
#endif

secret_cookie_not_found:
        if (sc[i].log == NGX_HTTP_SECRET_COOKIE_ON){
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "secret_cookie: no valid cookie \"%V\" in header",
&name);
        }
        continue;

secret_cookie_found:
        // Append the found secret_cookie's name into
secret_cookie_value:
        p = ngx_copy(secret_cookie_value.data + secret_cookie_value.len,
name.data, name.len);
        *p = (u_char) NGX_HTTP_SECRET_COOKIE_DIVIDER; // Name is
separate by a space
        secret_cookie_value.len += name.len + 1;
    }
    // Setup the secret_cookie_value variable:
    ctx->secret_cookie_value->len          = secret_cookie_value.len;
    ctx->secret_cookie_value->data         = secret_cookie_value.data;
    ctx->secret_cookie_value->valid        = 1;
    ctx->secret_cookie_value->no_cacheable = 1;
    ctx->secret_cookie_value->not_found    = 0;

#if (NGX_DEBUG)
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
               "secret_cookie: The final secret_cookie_value has
length=%i and value=%V", secret_cookie_value.len,
&secret_cookie_value);
#endif
    // Return the result
    return ctx;
}

/* Add $secret_cookie_set variable to the http process.
 * Called by "preconfiguration" hook!
 */
static ngx_int_t
ngx_http_secret_cookie_add_variable(ngx_conf_t *cf)
{
    ngx_http_secret_cookie_variable_t   *var;
    ngx_http_variable_t                 *v;

    for (var = ngx_http_secret_cookie_vars; var->name.len; var++) {

        v = ngx_http_add_variable(cf, &var->name,
NGX_HTTP_VAR_CHANGEABLE);
        if (v == NULL) {
            return NGX_ERROR;
        }

        v->get_handler = var->handler;
        v->data = var->data;
    }

    return NGX_OK;
}

/*
 * This function is called when location configuration is created.
 * It will create a default configuration for secret_cookie module.
 */
static void *
ngx_http_secret_cookie_create_conf(ngx_conf_t *cf)
{
    ngx_http_secret_cookie_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool,
sizeof(ngx_http_secret_cookie_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    /*
     * Set by ngx_pcalloc():
     *
     *   conf->secret_cookies = NULL;
     *   conf->secret_cookies_value_disabled = NULL;
     */
    conf->status = NGX_CONF_UNSET;
    return conf;
}

/*
 * This function is called when location configuration is merged to main
config.
 * It will create a default configuration for secret_cookie module.
 */
static char *
ngx_http_secret_cookie_merge_conf(ngx_conf_t *cf, void *parent, void
*child)
{
    ngx_http_secret_cookie_conf_t  *prev = parent;
    ngx_http_secret_cookie_conf_t  *conf = child;
    u_char                         *p;
    ngx_str_t                      secret_cookie_value_disabled;
    ngx_uint_t                     i;
    ngx_http_secret_cookie_t       *sc;

    // Merge previous to current configration, with a default value:
    ngx_conf_merge_value(conf->status, prev->status,
                              NGX_HTTP_SECRET_COOKIE_OFF);
    if (conf->secret_cookies == NULL){
        conf->secret_cookies = prev->secret_cookies;
        conf->secret_cookie_value_disabled =
prev->secret_cookie_value_disabled;
    } else {
        // Generate the disabled_value for this configuration:
        // First, count the required string length
        secret_cookie_value_disabled.len = 0;
        sc = conf->secret_cookies->elts;
        for (i=0; isecret_cookies->nelts; i++){
            secret_cookie_value_disabled.len += sc[i].name.len + 1;
        }
        // Secondly, create the buffer containing disabled_value

        secret_cookie_value_disabled.data = ngx_pcalloc(cf->pool,
secret_cookie_value_disabled.len);
        if (secret_cookie_value_disabled.data == NULL){
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "Not enough memory for
secret_cookie_value_disabled string");
            return NGX_CONF_ERROR;
        }
        // Thirdly, copy all secret cookie's name to disabled_value
        p = secret_cookie_value_disabled.data;
        for (i=0; isecret_cookies->nelts; i++){
            p = ngx_copy(p, sc[i].name.data, sc[i].name.len);
            *p = (u_char) NGX_HTTP_SECRET_COOKIE_DIVIDER; // Name is
separate by a space
            p++;
        }
#if (NGX_DEBUG)
        ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
              "String secret_cookie_value_disabled has length = %i and
value = %V", secret_cookie_value_disabled.len,
&secret_cookie_value_disabled);
#endif
        conf->secret_cookie_value_disabled = ngx_pcalloc(cf->pool,
sizeof(ngx_http_variable_value_t));
        if (conf->secret_cookie_value_disabled == NULL){
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "Not enough memory for
conf->secret_cookie_value_disabled variable");
            return NGX_CONF_ERROR;
        }
        conf->secret_cookie_value_disabled->len          =
secret_cookie_value_disabled.len;
        conf->secret_cookie_value_disabled->valid        = 1;
        conf->secret_cookie_value_disabled->no_cacheable = 1;
        conf->secret_cookie_value_disabled->not_found    = 0;
        conf->secret_cookie_value_disabled->data         =
secret_cookie_value_disabled.data;
    }

    return NGX_CONF_OK;
}

/*
 * This function is called after secret_cookie_def is passed
 * from config to module. Here you can validate the param, or change the

 * param to meaningful value.
 */
static char *
ngx_http_secret_cookie_def(ngx_conf_t *cf, ngx_command_t *cmd, void
*conf)
{
    ngx_http_secret_cookie_conf_t *sccf = conf;

    ngx_uint_t                    n, i, anumber;
    u_char                        c;
    ngx_str_t                     *value;
    ngx_http_secret_cookie_t      *sc;

    // Create secret_cookies array to hold definitions
    if (sccf->secret_cookies == NULL){
        sccf->secret_cookies = ngx_array_create(cf->pool, 3,

sizeof(ngx_http_secret_cookie_t));
        if (sccf->secret_cookies == NULL){
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

                        "Not enough memory for sccf->secret_cookies
array");
            return NGX_CONF_ERROR;
        }
    }
    // Create new element in the array to hold one definition
    sc = ngx_array_push(sccf->secret_cookies);
    if (sc == NULL) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                "Not enough memory for new element in
sccf->secret_cookies array");
        return NGX_CONF_ERROR;
    }
    // Process the parameters
    sc->status = NGX_HTTP_SECRET_COOKIE_ON;
    sc->log = NGX_HTTP_SECRET_COOKIE_OFF;
    sc->ua_limit = 0;
    value = cf->args->elts;
    for (n=1; n < cf->args->nelts; n++){
        // off
        if (ngx_strncmp(value[n].data,"off",3)==0){
            sc->status = NGX_HTTP_SECRET_COOKIE_OFF;
            continue;
        }
        // log
        if (ngx_strncmp(value[n].data,"log=on",6)==0){
            sc->log = NGX_HTTP_SECRET_COOKIE_ON;
            continue;
        }
        //ua_limit=
        if (ngx_strncmp(value[n].data, "ua_limit=", 9) == 0){
            // Convert ua_limit to number:
            anumber = 0;
            for (i=9; i < value[n].len; i++){
                c = value[n].data[i];
                if (c >= '0' && c <= '9'){
                    anumber = anumber*10 + (c - '0');
                    continue;
                }
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"ua_limit\" (must be an integer)");
                return NGX_CONF_ERROR;
            }
            // Validate the ua_limit:
            if ((anumber > 0) && (anumber <
NGX_HTTP_SECRET_COOKIE_MINIMUM_UA_LIMIT)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"ua_limit\" (must be greater than
%i)", NGX_HTTP_SECRET_COOKIE_MINIMUM_UA_LIMIT);
                return NGX_CONF_ERROR;
            }
            sc->ua_limit = anumber;
            continue;
        }
        // name=
        if (ngx_strncmp(value[n].data, "name=", 5) == 0){
            sc->name.len  = value[n].len - 5;
            if ((sc->name.len < 1) || (sc->name.len >
NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Parameter \"name\" is empty or too long");
                return NGX_CONF_ERROR;
            }
            sc->name.data = value[n].data + 5;
            continue;
        }
        // salt=
        if (ngx_strncmp(value[n].data, "salt=", 5) == 0){
            sc->salt.len  = value[n].len - 5;
            if ((sc->salt.len < 1) || (sc->salt.len >
NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Parameter \"salt\" is empty or too long");
                return NGX_CONF_ERROR;
            }
            sc->salt.data = value[n].data + 5;
            continue;
        }
        // rule=
        if (ngx_strncmp(value[n].data, "rule=", 5) == 0){
            // Validate the rule:
            for (i=5; i < value[n].len; i++){
                c = ngx_tolower(value[n].data[i]);
                switch (c){
                    case NGX_HTTP_SECRET_COOKIE_RULE_ADDRESS:
                    case NGX_HTTP_SECRET_COOKIE_RULE_TIME:
                    case NGX_HTTP_SECRET_COOKIE_RULE_SALT:
                    case NGX_HTTP_SECRET_COOKIE_RULE_USER_AGENT:
                        break;
                    default:
                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "Rule does not accept characters other than
\"a\", \"t\", \"s\" and \"u\"");
                        return NGX_CONF_ERROR;
                }
            }

            sc->rule.len  = value[n].len - 5;
            if ((sc->rule.len < 1) || (sc->rule.len >
NGX_HTTP_SECRET_COOKIE_MAX_SHORT_STR_SIZE)){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Parameter \"rule\" is empty or too long");
                return NGX_CONF_ERROR;
            }
            sc->rule.data = value[n].data + 5;
            continue;
        }
        // duration
        if (ngx_strncmp(value[n].data, "duration=", 9) == 0){
            // Convert duration to number:
            anumber = 0;
            for (i=9; i < value[n].len; i++){
                c = value[n].data[i];
                if (c >= '0' && c <= '9'){
                    anumber = anumber*10 + (c - '0');
                    continue;
                }
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"duration\" (must be an integer)");
                return NGX_CONF_ERROR;
            }
            // Validate the duration:
            if (anumber < NGX_HTTP_SECRET_COOKIE_MINIMUM_DURATION){
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "Invalid \"duration\" (must be greater than
%i)", NGX_HTTP_SECRET_COOKIE_MINIMUM_DURATION);
                return NGX_CONF_ERROR;
            }
            sc->duration = anumber;
            continue;
        }
    }

    if (sc->rule.data == NULL){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"rule\" is
missing!");
        return NGX_CONF_ERROR;
    }
    if (sc->salt.data == NULL){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"salt\" is
missing!");
        return NGX_CONF_ERROR;
    }
    if (sc->name.data == NULL){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"name\" is
missing!");
        return NGX_CONF_ERROR;
    }
    if (sc->duration == 0){
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "The required parameter \"duration\" is
missing");
        return NGX_CONF_ERROR;
    }
#if (NGX_DEBUG)
    ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0,
                   "Secret_cookie_def: on/off=%i name=%V salt=%V rule=%V
duration=%i",
                   sc->status, &sc->name, &sc->salt, &sc->rule,
sc->duration);
#endif
    return NGX_CONF_OK;
}

/*
 * Convert an int64 to string presentation
 */
static u_char * ngx_num2str(u_char *buf, u_char *last, uint64_t ui64)
{
    u_char         *p, temp[NGX_INT64_LEN + 1];
                       /*
                        * we need temp[NGX_INT64_LEN] only,
                        * but icc issues the warning
                        */
    size_t          len;
    uint32_t        ui32;

    p = temp + NGX_INT64_LEN;

    if (ui64 <= NGX_MAX_UINT32_VALUE) {
        ui32 = (uint32_t) ui64;

        do {
            *--p = (u_char) (ui32 % 10 + '0');
        } while (ui32 /= 10);

    } else {
        do {
            *--p = (u_char) (ui64 % 10 + '0');
        } while (ui64 /= 10);
    }

    /* number safe copy */

    len = (temp + NGX_INT64_LEN) - p;

    if (buf + len > last) {
        len = last - buf;
    }

    return ngx_cpymem(buf, p, len);
}

/*
 * Comparison of a raw string and a hex-style string
 */
static ngx_int_t ngx_raw_vs_hex(u_char *rawbuf, size_t rlen, u_char
*hexbuf, size_t hlen)
{
    static u_char    hex[] = "0123456789abcdef";
    u_char           hexchar[] = "aa";
    ngx_int_t        len, i;
    // Compare by the shortest length
    len = ((rlen < hlen / 2) ? rlen : hlen / 2);
    i = 0;
    while (i < len){
        // Calculate a hexchar from rawbuff
        hexchar[1] = hex[rawbuf[i] & 0xf];
        hexchar[0] = hex[(rawbuf[i]>>4) & 0xf];
        if ((hexchar[0] != hexbuf[2*i]) || (hexchar[1] !=
hexbuf[2*i+1])){
            return 1;
        }
        i++;
    }
    return 0;
}

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,55378,173796#msg-173796

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs