NAXSI setup howto

This document describes the full process of configuring NAXSI.

Installing nginx + naxsi : From package

Packages of naxsi exist for :

  • Debian
  • FreeBSD
  • NetBSD
  • CentOS/Redhat (See axivo repositories)

 

If you’re unlucky, refer to source compilation.

Installing nginx + naxsi : From sources

Nginx doesn’t support (by design) loadable modules. Extra modules must be added during compilation. Here we will install it from the source, but (if you’re lucky) you might as well find nginx+naxsi already packaged in your favorite distribution.

If you’re not, here is the way to go :

wget http://nginx.org/download/nginx-x.x.xx.tar.gz
wget http://naxsi.googlecode.com/files/naxsi-x.xx.tar.gz
tar xvzf nginx-x.x.xx.tar.gz 
tar xvzf naxsi-x.xx.tar.gz
cd nginx-x.x.xx/

[install libpcre (and libssl if you want https, along with zlib for gzip support) libs with your favorite package manager, naxsi relies on it for regex]

./configure --add-module=../naxsi-x.xx/naxsi_src/ [add/remove your favorite/usual options]
make
make install

my personal “building” options being, here :

./configure --conf-path=/etc/nginx/nginx.conf  --add-module=../naxsi-x.xx/naxsi_src/   --error-log-path=/var/log/nginx/error.log     --http-client-body-temp-path=/var/lib/nginx/body     --http-fastcgi-temp-path=/var/lib/nginx/fastcgi     --http-log-path=/var/log/nginx/access.log     --http-proxy-temp-path=/var/lib/nginx/proxy     --lock-path=/var/lock/nginx.lock     --pid-path=/var/run/nginx.pid     --with-http_ssl_module     --without-mail_pop3_module     --without-mail_smtp_module     --without-mail_imap_module     --without-http_uwsgi_module     --without-http_scgi_module     --with-ipv6  --prefix=/usr

Important note for source compilation

You need to remember this if you are new to nginx :

NGINX will decide the order of modules according the order of the module’s directive in nginx’s ./configure. So, no matter what (except you reallyknow what you are doing) put naxsi first in your ./configure.

If you don’t do so, you might run into various problems, from random / unpredictable behaviors to non-effective WAF.

Initial setup

I want to configure NAXSI for my company’s website :

www.nbs-system.com

So, let’s have a look at the initial setup :

/etc/nginx/nginx.conf :

user www-data;
worker_processes  1;
worker_rlimit_core  500M;
working_directory   /tmp/;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
    use epoll;
    # multi_accept on;                                                                                                                      
}

http {
    include        /etc/nginx/naxsi_core.rules;
    include       /etc/nginx/mime.types;
    server_names_hash_bucket_size 128;
    access_log  /var/log/nginx/access.log;

    sendfile        on;
    keepalive_timeout  65;
    tcp_nodelay        on;

    gzip  on;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    include /etc/nginx/sites-enabled/*;
}

Notice the /etc/nginx/naxsi_core.rules include. This file is provided in the project (naxsi_config/), and it contains the rules. As you might have noticed, these are not signatures, in the classic WAF sense, but simple “score rules”.

Now, let’s have a look at my sites-enabled/default :

server {
 proxy_set_header Proxy-Connection "";
listen       *:80;
access_log  /tmp/nginx_access.log;
error_log  /tmp/nginx_error.log debug;

location / {
     include    /etc/nginx/nbs.rules;
     proxy_pass http://194.213.124.111/;
     proxy_set_header Host www.nbs-system.com;
   }

#This location is where, in learning mode, to-be-forbidden requests will be "copied"
#In non-learning mode, it's where denied request will land, so feel free to do whatever you want, 
#return 418 I'm a teapot, forward to a custom webpage with 
#a captcha to help track false-positives (see contrib for that),
#whatever you want to do !
 location /RequestDenied {
     proxy_pass http://127.0.0.1:4242;
   }
}

/etc/nginx/nbs.rules :

LearningMode; #Enables learning mode
SecRulesEnabled;
#SecRulesDisabled;
DeniedUrl "/RequestDenied";

include "/tmp/naxsi_rules.tmp";

## check rules
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;

/tmp/naxsi_rules.tmp is empty for now, it’ll be filled at runtime by the learning daemon.

Starting the LearningMode phase

Once you performed a bit of browsing, learning daemons db will be populated with generated exceptions. You can as well populate naxsi’s db from log files (options -l of nx_intercept). If you have real users on your website and/or you are not in a hurry, this option might be better, as it allows you not to spend time to do the whitelist configuration. See LearningFromLogFiles for details.

The web interface is minimalist, and has the following features :

  • Whitelist generation : Main goal of the daemon. From naxsi catched exceptions, generate approriate whitelists. Rules are presented in raw form, as well as in optimized form. For example, after some browsing I got the following rules :
 ########### Optimized Rules Suggestion ##################
 # total_count:59 (23.69%), peer_count:1 (100.0%) | parenthesis, probable sql/xss
 BasicRule wl:1011 "mz:$HEADERS_VAR:cookie";
 # total_count:59 (23.69%), peer_count:1 (100.0%) | parenthesis, probable sql/xss
 BasicRule wl:1010 "mz:$HEADERS_VAR:cookie";
 # total_count:59 (23.69%), peer_count:1 (100.0%) | mysql keyword (|)
 BasicRule wl:1005 "mz:$HEADERS_VAR:cookie";
 # total_count:53 (21.29%), peer_count:1 (100.0%) | double encoding !
 BasicRule wl:1315 "mz:$HEADERS_VAR:cookie";

 

  • Statistics generation : You can get reports on source / types of attacks, as well as counts for each kind of exceptions :

The global idea, indeed, is to use the whitelists generated by naxsi, include them into your naxsi’s configuration, and then reload nginx.

For advanced whitelists, such as user forms, please see AdvancedWhitelists section. Following section deals as well with user forms in a more classic way.

user forms

Now comes the “tricky” part of whitelists triggers : USER FORMS !

Yes, fields with ‘free’ user input, such as registration forms, search boxes and so on are typically parts that you should take a great care of.

For example, my company’s website contains a “contact” form with lastname, firstname, email adress, and a free text zone. I decided that I will allow simple/double quotes as well as coma and semi-coma in the last/first names fields, and included as well parenthesis for the free text zone. So, I will simply fill the form and learnign daemons will generate the associated whitelists.

Once I’ve filled the forms, if I look at my nx_extract interface, I will see that new exceptions have been generated :

rule 1007(--) authorized on url for argument 'cf2_field_1' of zone BODY
rule 1010(() authorized on url for argument 'cf2_field_1' of zone BODY
rule 1011()) authorized on url for argument 'cf2_field_1' of zone BODY
rule 1013(') authorized on url for argument 'cf2_field_1' of zone BODY
rule 1015(,) authorized on url for argument 'cf2_field_1' of zone BODY
rule 1306(') authorized on url for argument 'cf2_field_1' of zone BODY
rule 1308(() authorized on url for argument 'cf2_field_1' of zone BODY
rule 1309()) authorized on url for argument 'cf2_field_1' of zone BODY
rule 1007(--) authorized on url for argument 'cf2_field_2' of zone BODY
rule 1013(') authorized on url for argument 'cf2_field_2' of zone BODY
rule 1015(,) authorized on url for argument 'cf2_field_2' of zone BODY
rule 1306(') authorized on url for argument 'cf2_field_2' of zone BODY
rule 1007(--) authorized on url for argument 'cf2_field_3' of zone BODY
rule 1013(') authorized on url for argument 'cf2_field_3' of zone BODY
rule 1015(,) authorized on url for argument 'cf2_field_3' of zone BODY
rule 1306(') authorized on url for argument 'cf2_field_3' of zone BODY
rule 1007(--) authorized on url for argument 'cf2_field_4' of zone BODY
rule 1007(--) authorized on url for argument 'cf2_field_5' of zone BODY
rule 1007(--) authorized on url for argument 'cf2_field_7' of zone BODY
rule 1010(() authorized on url for argument 'cf2_field_7' of zone BODY
rule 1011()) authorized on url for argument 'cf2_field_7' of zone BODY
rule 1013(') authorized on url for argument 'cf2_field_7' of zone BODY
rule 1015(,) authorized on url for argument 'cf2_field_7' of zone BODY
rule 1306(') authorized on url for argument 'cf2_field_7' of zone BODY
rule 1308(() authorized on url for argument 'cf2_field_7' of zone BODY
rule 1309()) authorized on url for argument 'cf2_field_7' of zone BODY
rule 1007(--) authorized on url for argument 'cf_codeerr2' of zone BODY
rule 1315() authorized on url for argument 'cf_codeerr2' of zone BODY
rule 1315() authorized on url for argument 'cf_failure2' of zone BODY
rule 1200(..) authorized on url for argument 'cf_working2' of zone BODY
rule 1315() authorized on url for argument 'cf_working2' of zone BODY

Let’s reload it, and have a look at the generated whitelists ! New rules have been generated, in the style :

BasicRule wl:1007 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1010 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1011 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1013 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1015 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1306 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1308 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1309 "mz:$BODY_VAR:cf2_field_1" ;
BasicRule wl:1007 "mz:$BODY_VAR:cf2_field_2" ;
BasicRule wl:1013 "mz:$BODY_VAR:cf2_field_2" ;
BasicRule wl:1015 "mz:$BODY_VAR:cf2_field_2" ;
BasicRule wl:1306 "mz:$BODY_VAR:cf2_field_2" ;
BasicRule wl:1007 "mz:$BODY_VAR:cf2_field_3" ;
BasicRule wl:1013 "mz:$BODY_VAR:cf2_field_3" ;
BasicRule wl:1015 "mz:$BODY_VAR:cf2_field_3" ;
BasicRule wl:1306 "mz:$BODY_VAR:cf2_field_3" ;
BasicRule wl:1007 "mz:$BODY_VAR:cf2_field_4" ;
BasicRule wl:1007 "mz:$BODY_VAR:cf2_field_5" ;
BasicRule wl:1007 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1010 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1011 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1013 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1015 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1306 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1308 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1309 "mz:$BODY_VAR:cf2_field_7" ;
BasicRule wl:1007 "mz:$BODY_VAR:cf_codeerr2" ;
BasicRule wl:1315 "mz:$BODY_VAR:cf_codeerr2" ;
BasicRule wl:1315 "mz:$BODY_VAR:cf_failure2" ;
BasicRule wl:1200 "mz:$BODY_VAR:cf_working2" ;
BasicRule wl:1315 "mz:$BODY_VAR:cf_working2" ;

Once I’ve did the same for the searchbox, my configuration is now over, and we can browse the site, and fill the forms without generating any new exception !

Some side notes

Sometimes, you will want to partially disable naxsi for a part of the website. In the case of my company’s website, I don’t want to configure / enable naxsi for the wordpress back-office, as it’s already protected by a .htaccess.

Then, you can “simply” define another location, where you don’t enable NAXSI :

location / {
     include    /etc/nginx/nbs.rules;
     proxy_pass http://194.213.124.111/;
     proxy_set_header Host www.nbs-system.com;
   }

location /wp-admin {
     proxy_pass https://194.213.124.111/;
     proxy_set_header Host www.nbs-system.com;
 }

And the trick is done 😉

Actually, you can do something way smarter. As wordpress is affected by numerous vulnerabilities in the back-office, I still want to protect it, but without spending too much time on the configuration, so here is what I’m doing :

location /wp-admin {
         include /etc/nginx/nbs.rules;
         BasicRule wl:0 mz:BODY;
         proxy_pass https://194.213.124.111;
         proxy_set_header Host www.nbs-system.com;
}

I’m enabling NAXSI, but I’m disabling all checks on BODY, as it’s the painfull part (posting HTML and so on). In this way, I will still protect WP back-office from vulnerabilities that are exploited through GET requests.

google文档地址:http://code.google.com/p/naxsi/



分享到: 更多

这篇日志的 QR 二维码为:

一月 18th, 2013

Posted In: linux系统

发表评论

电子邮件地址不会被公开。 必填项已用*标注

无觅相关文章插件,快速提升流量