A Mechanism to Help Write Web Application Firewalls for Nginx

Developing a Web Application Firewall module for Nginx is not an easy task. The lack of input body filters makes it harder. Nginx is an outstanding web server, but it is not perfect. Actually, nothing is perfect.

So we added the input body filter mechanism to our own Nginx distribution, which is named Tengine. By taking advantage of this mechanism, processing the request body is not that complicated anymore (In standard Nginx, request body may be buffered to disk file and you have to deal with up to two buffers)

Here I have an example to demonstrate how to write an input body filter. It is a simple module to fight hash collision DoS attacks.

/*
 * Copyright (C) Joshua Zhu, http://www.zhuzhaoyuan.com
 */
 
 
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
 
 
typedef struct {
    ngx_flag_t                            enable;
    ngx_uint_t                            max_post_params;
} ngx_http_anti_hashdos_loc_conf_t;
 
 
typedef struct {
    ngx_uint_t                            post_params;
} ngx_http_anti_hashdos_ctx_t;
 
 
static ngx_int_t ngx_http_anti_hashdos_input_body_filter(ngx_http_request_t *r,
    ngx_buf_t *buf);
 
static void *ngx_http_anti_hashdos_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_anti_hashdos_merge_loc_conf(ngx_conf_t *cf, void *parent,
    void *child);
static ngx_int_t ngx_http_anti_hashdos_init(ngx_conf_t *cf);
 
 
static ngx_command_t ngx_http_anti_hashdos_filter_commands[] = {
 
    { ngx_string("anti_hashdos"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_anti_hashdos_loc_conf_t, enable),
      NULL },
 
    { ngx_string("anti_hashdos_max_post_params"),
      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_anti_hashdos_loc_conf_t, max_post_params),
      NULL },
 
      ngx_null_command
};
 
 
static ngx_http_module_t ngx_http_anti_hashdos_filter_module_ctx = {
    NULL,                                 /* preconfiguration */
    ngx_http_anti_hashdos_init,           /* postconfiguration */
 
    NULL,                                 /* create main configuration */
    NULL,                                 /* init main configuration */
 
    NULL,                                 /* create server configuration */
    NULL,                                 /* merge server configuration */
 
    ngx_http_anti_hashdos_create_loc_conf,/* create location configuration */
    ngx_http_anti_hashdos_merge_loc_conf  /* merge location configuration */
};
 
 
ngx_module_t ngx_http_anti_hashdos_filter_module = {
    NGX_MODULE_V1,
    &ngx_http_anti_hashdos_filter_module_ctx, /* module context */
    ngx_http_anti_hashdos_filter_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_input_body_filter_pt  ngx_http_next_input_body_filter;
 
 
static ngx_int_t
ngx_http_anti_hashdos_input_body_filter(ngx_http_request_t *r,
    ngx_buf_t *buf)
{
    u_char                           *p;
    ngx_http_anti_hashdos_ctx_t      *ctx;
    ngx_http_anti_hashdos_loc_conf_t *ahlf;
 
    ahlf = ngx_http_get_module_loc_conf(r, ngx_http_anti_hashdos_filter_module);
 
    if (!ahlf->enable) {
        return ngx_http_next_input_body_filter(r, buf);
    }
 
    ctx = ngx_http_get_module_ctx(r, ngx_http_anti_hashdos_filter_module);
    if (ctx == NULL) {
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_anti_hashdos_ctx_t));
        if (ctx == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
 
        ctx->post_params = 1;
    }
 
    for (p = buf->pos; p < buf->last; p++) {
 
        if (*p == '&') {
            ctx->post_params++;
        }
    }
 
    if (ctx->post_params > ahlf->max_post_params) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "anti hashdos: \"%V\" blocked, too many post params: %d",
                      &r->connection->addr_text,
                      ctx->post_params);
        return NGX_HTTP_BAD_REQUEST;
    }
 
    return ngx_http_next_input_body_filter(r, buf);
}
 
 
static void *
ngx_http_anti_hashdos_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_anti_hashdos_loc_conf_t *conf;
 
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_anti_hashdos_loc_conf_t));
    if (conf == NULL) {
        return NULL;
    }
 
    conf->enable = NGX_CONF_UNSET;
    conf->max_post_params = NGX_CONF_UNSET_UINT;
 
    return conf;
}
 
 
static char *
ngx_http_anti_hashdos_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_anti_hashdos_loc_conf_t *prev = parent;
    ngx_http_anti_hashdos_loc_conf_t *conf = child;
 
    ngx_conf_merge_value(conf->enable, prev->enable, 0);
    ngx_conf_merge_uint_value(conf->max_post_params, prev->max_post_params,
                              120);
 
    return NGX_CONF_OK;
}
 
 
static ngx_int_t
ngx_http_anti_hashdos_init(ngx_conf_t *cf)
{
    ngx_http_next_input_body_filter = ngx_http_top_input_body_filter;
    ngx_http_top_input_body_filter = ngx_http_anti_hashdos_input_body_filter;
 
    return NGX_OK;
}

The code looks quite straight forward, right? And it is similar to an output body filter, just a few steps:

1) Implement your own input body filter function. e.g.

static ngx_int_t
ngx_http_anti_hashdos_input_body_filter(ngx_http_request_t *r, ngx_buf_t *buf)
{
    /* Do the input body filtering here */
}

2) In your input body filter function, return an HTTP error code if something is wrong. Otherwise, call ngx_http_next_input_body_filter(r, buf) directly to pass the buf to the next input body filters.

3) Install your input body filter in the post_configuration hook function. Push your input body filter to the head of the input body filter chain. e.g.

static ngx_int_t
ngx_http_anti_hashdos_init(ngx_conf_t *cf)
{
    ngx_http_next_input_body_filter = ngx_http_top_input_body_filter;
    ngx_http_top_input_body_filter = ngx_http_anti_hashdos_input_body_filter;
 
    return NGX_OK;
}

NOTE: This is just a demonstration to show how to write input body filters. If you want to fight hash collision DoS attacks completely, you have to write more code and various POST content types should be processed.

Download the code here:
http://www.zhuzhaoyuan.com/download/tengine/anti_hashdos.tar.gz

53 Comments »

  1. james said,

    January 18, 2012 @ 9:20 am

    #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)

    请教,为什么是这样

  2. Joshua said,

    January 18, 2012 @ 9:50 am

    @james,
    大于PAGE_SIZE或pool的内存nginx会单独存放一个链表,这个是可能被回收的,以节约内存。请阅读src/core/ngx_palloc.c和我之前写的nginx internals文档中讲内存池实现部分

  3. james said,

    January 18, 2012 @ 10:57 am

    我的意思是 比如linux的pagesize是 4k ,它为什么是4k-1

  4. Joshua said,

    January 18, 2012 @ 11:25 am

    @james,
    >= PAGE_SIZE的内存回收更有意义

  5. Jason.Lee said,

    January 18, 2012 @ 2:20 pm

    不仅仅是佩服

  6. tricky1997 said,

    January 19, 2012 @ 6:15 pm

    请问,为什么不通过入侵检测工具(例如snort)来防止hash Dos,而要在nginx里加功能呢?

  7. Abioy said,

    February 12, 2012 @ 10:21 am

    Cool! Tengine把input filter给做了?我们某个服务由于已经在ACCESS PHASE中处理body,便直接在该PHASE加入了防御。

  8. Joshua said,

    February 12, 2012 @ 3:05 pm

    @Abioy:
    是的。在Nginx的phase里是无法完美处理body的,所以我们把input body filter加上了 :)

  9. Anders said,

    February 19, 2012 @ 5:47 am

    sorry for posting unrelated question here:

    First of all, your blog has been very helpful for nginx newbies. Thanks a lot!

    I am playing with your hello world module and attached it with NGX_HTTP_ACCESS_PHASE. As you may know, if you apply the basic auth on a uri that is backed by a remote resource through reversed proxy, the authentication header is carried over and hit the remote resource. For lots of cases, this will invoke the auth mechanism of the remote resource, say tomcat.

    I understand you can bypass it by proxy_set_header Authorization “”; in the config file. But I am wondering if there is a way to suppress/remove it from the header_in in my module?

    I tried: r->headers_in.authorization->value.len=0;

    It appears to work. But I don’t know this could lead any memory leak since the authorization is a pointer and probably the memory is dynamically allocated for the request.

    Could you shed some light here?

    Thxs!

    Anders

  10. erica said,

    June 22, 2012 @ 6:33 am

    Very useful post, I’ve been looking to do something with nginx for a while but just needed a little help. thank you very much.

  11. Dheel said,

    June 23, 2012 @ 2:47 am

    This is genuinely fascinating, you might be a very professional blogger. I’ve joined your rss feed and will be searching for a lot of your fantastic posts. Also, I have shared your web site in my social networks!

  12. designer prescription glasses said,

    June 23, 2012 @ 10:57 pm

    I am usually to running a blog and i actually admire your content. The article has actually peaks my interest. I’m going to bookmark your web site and hold checking for new information.

  13. Garmin 1490t said,

    June 24, 2012 @ 1:00 am

    Hey admin, incredibly informative blog post! Pleasee continue this awesome work..

  14. tulsa dentists said,

    June 24, 2012 @ 2:30 am

    It’s the best time to make some plans for the future and it’s time to be happy. I’ve read this post and if I could I wish to suggest you few interesting things or advice. Perhaps you can write next articles referring to this article. I want to read more things about it!

  15. grout stain said,

    June 24, 2012 @ 8:32 am

    It is really a nice and helpful piece of info. I’m happy that you just shared this helpful info with us. Please keep us informed like this. Thank you for sharing.

  16. http://www.freebootlegmovies.org/ said,

    June 24, 2012 @ 9:11 am

    I just like the helpful info you provide in your articles. I’ll bookmark your weblog and test once more here frequently. I am reasonably certain I’ll be informed plenty of new stuff right right here! Good luck for the following!

  17. Edmonton Web Design said,

    June 26, 2012 @ 4:20 pm

    When looking for a web hosting provider, a good indicator of a professional firm is a regularly updated blog on the provider’s website. Although it may be overlooked, a blog indicates that the provider is willing to maintain communication, whether it’s about server issues, upgrades, or new features that are on the way. Also look for professional responses to comments left for the provider by other users.

  18. Time Share Relief said,

    June 28, 2012 @ 3:31 am

    Some really great articles on this web site, thanks for contribution. “Gratitude is merely the secret hope of further favors.” by La Rochefoucauld.

  19. Selling a Timeshare said,

    June 28, 2012 @ 6:38 am

    I truly enjoy looking through on this web site , it holds good posts . “We find comfort among those who agree with us–growth among those who don’t.” by Frank A. Clark.

  20. Receding gums said,

    June 28, 2012 @ 8:59 pm

    You are my breathing in, I have few blogs and sometimes run out from brand :). “To die for a religion is easier than to live it absolutely.” by Jorge Luis Borges.

  21. Timeshare Relief Now said,

    June 29, 2012 @ 1:43 am

    I see something truly interesting about your weblog so I saved to favorites .

  22. Challenge Coins said,

    June 29, 2012 @ 2:01 am

    Ohh really Interesting and intellectually derived this coding,,, Much ‘ll be helpful great post and very useful as well.

  23. u.s. challenge coins, llc - home ... said,

    June 29, 2012 @ 3:27 am

    It’s actually a great and useful piece of info. I’m glad that you simply shared this helpful information with us. Please keep us informed like this. Thanks for sharing.

  24. lapel pins said,

    June 29, 2012 @ 3:34 am

    Thanks designed for sharing such a good thinking, post is pleasant,
    thats why i have read it completely

  25. be domeinregistratie said,

    June 29, 2012 @ 3:40 am

    Thank you, I’ve recently been searching for info about this topic for ages and yours is the greatest I’ve discovered till now. But, what about the bottom line? Are you sure about the source?

  26. seo firm said,

    June 29, 2012 @ 3:44 am

    An impressive share! I have just forwarded this onto a coworker who had been doing a little homework on this.
    And he in fact ordered me lunch because I stumbled upon it for
    him… lol. So allow me to reword this…. Thank YOU for the
    meal!! But yeah, thanks for spending time to talk about this matter here on your site.

  27. seo firms said,

    June 29, 2012 @ 3:45 am

    This paragraph will help the internet visitors for building up new web site or even a blog from
    start to end.

  28. How To Get Rid Of A Timeshare said,

    June 29, 2012 @ 4:43 am

    Rattling nice style and design and good articles , nothing else we need : D.

  29. painting colorado springs said,

    July 2, 2012 @ 4:51 am

    Respect to website author , some wonderful entropy. “Consider that this day ne’er dawns again.” by Alighieri Dante.

  30. http://freebootlegmovies.org/ said,

    July 3, 2012 @ 12:45 am

    Your home is valueble for me. Thanks!…

  31. http://www.freebootlegmovies.org/ said,

    July 3, 2012 @ 1:09 am

    You need to take part in a contest for among the best blogs on the web. I will advocate this site!

  32. Vern Hargrove said,

    July 3, 2012 @ 9:38 pm

    Hi there. I discovered your website by means of Google even as looking for a similar matter, your web site came up. It seems great. I have bookmarked it in my google bookmarks to come back later.

  33. pozycjonowanie www kraków said,

    July 4, 2012 @ 9:46 am

    Thanks for the good writeup. It in truth was once a leisure account it. Glance complex to far delivered agreeable from you! However, how can we be in contact?

  34. Sunglasses Sale said,

    August 6, 2012 @ 5:38 pm

    XIEXIE,支持

  35. celine boston said,

    September 24, 2012 @ 10:49 am

    good

  36. Herma Shafer said,

    October 19, 2012 @ 10:56 am

    Hey! I just would like to give a huge thumbs up for the nice data you have got here on this post. I will probably be coming again to your weblog for more soon.

  37. Jolanda Samantha said,

    October 19, 2012 @ 9:51 pm

    Very efficiently written information. It will be valuable to everyone who utilizes it, including me. Keep doing what you are doing - i will definitely read more posts.

  38. Shara Verley said,

    October 19, 2012 @ 10:09 pm

    I think this is among the most vital info for me. And i’m glad reading your article. But wanna remark on few general things, The web site style is ideal, the articles is really nice : D. Good job, cheers

  39. Crystal Priesmeyer said,

    October 19, 2012 @ 10:37 pm

    Hey very cool site!! Man .. Excellent .. Amazing .. I will bookmark your blog and take the feeds also…I’m happy to find so many useful info here in the post, we need develop more techniques in this regard, thanks for sharing. . . . . .

  40. Inez Banes said,

    October 25, 2012 @ 5:51 am

    I’d must verify with you here. Which isn’t something I usually do! I enjoy reading a put up that will make folks think. Also, thanks for allowing me to comment!

  41. Ciara Waltemath said,

    October 26, 2012 @ 1:51 am

    What i don’t realize is actually how you are not really much more well-liked than you might be now. You are so intelligent. You realize thus significantly relating to this subject, made me personally consider it from a lot of varied angles. Its like women and men aren’t fascinated unless it is one thing to do with Lady gaga! Your own stuffs outstanding. Always maintain it up!

  42. Geneva Sydney said,

    October 27, 2012 @ 8:24 am

    I must voice my passion for your kind-heartedness giving support to people who really need assistance with the idea. Your personal commitment to getting the solution up and down came to be definitely insightful and has always encouraged guys much like me to get to their pursuits. Your new important instruction means a great deal a person like me and especially to my mates. Thank you; from all of us.

  43. Andree Tromburg said,

    October 27, 2012 @ 10:59 am

    Just want to say your article is as surprising. The clearness in your post is just excellent and i can assume you’re an expert on this subject. Well with your permission allow me to grab your feed to keep updated with forthcoming post. Thanks a million and please continue the rewarding work.

  44. Lulu Baumgardt said,

    October 27, 2012 @ 12:44 pm

    Great – I should certainly pronounce, impressed with your web site. I had no trouble navigating through all tabs as well as related information ended up being truly easy to do to access. I recently found what I hoped for before you know it at all. Reasonably unusual. Is likely to appreciate it for those who add forums or something, site theme . a tones way for your customer to communicate. Excellent task..

  45. Vesta Aimbez said,

    October 27, 2012 @ 11:11 pm

    Hello, i think that i saw you visited my blog so i came to “return the favor”.I’m trying to find things to enhance my website!I suppose its ok to use some of your ideas!!

  46. Davida Senno said,

    October 30, 2012 @ 5:07 pm

    Hey! I just would like to give an enormous thumbs up for the good info you could have right here on this post. I might be coming back to your blog for extra soon.

  47. Melia Stivason said,

    October 31, 2012 @ 2:35 am

    Thanks for sharing superb informations. Your site is so cool. I am impressed by the details that you have on this website. It reveals how nicely you perceive this subject. Bookmarked this web page, will come back for extra articles. You, my friend, ROCK! I found just the information I already searched everywhere and simply couldn’t come across. What an ideal web-site.

  48. http://oficity.it said,

    December 26, 2012 @ 7:49 pm

    Hello, i think that i saw you visited my blog so i came to “return the favor”.I’m trying to find things to enhance my website!I suppose its ok to use some of your ideas!!

  49. niq said,

    February 25, 2013 @ 6:43 am

    (I arrived here after posting about ironbee on nginx, when someone pointed me to tengine).

    You should clear the spam from this post. The last non-spam comment here is Anders, from Feb. 19th 2012. Once you have one spam comment, the bots will find it and add more.

  50. xn--b1ag0adlg.xn--p1ai said,

    July 26, 2013 @ 4:00 am

    In contemporary times, videoke sessions became a staple entertainment for all occasions.
    In internal retention, pulll the abdominal organs in and
    up, and simultaneously bring the lower spine forward.
    torrent files to download any kind data straight from other peers.

  51. homepage said,

    October 18, 2013 @ 6:29 am

    It’s actually very complicated in this full of
    activity life to listen news on TV, therefore I simply use the web for that purpose, and get the
    latest news.

  52. WAF Comodo said,

    December 31, 2013 @ 7:45 am

    Great article, it summarizes very well the WAF. No
    matter how it could be, compared to proxying everything through a WAF point.

  53. http://www.loveyourhaircassandra.com said,

    February 11, 2014 @ 2:05 am

    DASHCOM websites are springing up throughout the world.
    I am not just a PC player, please anyone assist me to remove Dell Inspiron
    password…” We usually hear such inquiry around us.
    The Vi - O comes pre set most abundant in widely
    used mobile phones, such as Blackberry, i - Pod, i - Phone amongst others.

RSS feed for comments on this post · TrackBack URI

Leave a Comment

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word