forked from nbs-system/naxsi
-
Notifications
You must be signed in to change notification settings - Fork 0
NAXSI is an open-source, high performance, low rules maintenance WAF for NGINX
naxsi.googlecode.com github.com/nbs-system/naxsi
License
blotus/naxsi
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
_ _ _ | \ | | __ ___ _____(_) | \| |/ _` \ \/ / __| | | |\ | (_| |> <\__ \ | |_| \_|\__,_/_/\_\___/_| v0.1 alpha [[[!!! PLEASE REFER TO THE WIKI INSTEAD !!!]]] Yes, this readme is for v0.1 alpha, now is 0.46-1 |--{ Introduction }------------------------------------------------------------| NAXSI is a module for nginx, the famous webserver/reverse-proxy/... Its goal is to help people to secure their web application against attacks such as SQL Injection, Cross Site Scripting, Cross Site Request Forgery, Local & Remote file inclusions and such. The difference with most WAF (Web Applicative Firewalls) out there is that it does not rely on signatures to detect attacks. It is using a simpler model, where instead of trying to detect "known" attacks, it will detect unexpected characters in the HTTP request/arguments. Each kind of unusual character will increase the score of the request. If the request reaches a score that's considered "too high", the request will be denied, and the user will be redirected to a "forbidden" page. Yes, it works a bit like a spam system. |--{ Why is it different ? }--------------------------------------------------| NAXSI is different, because it works on a learning mode (read whitelist). Set the module in learning mode, scroll your site, and it will generate the necessaries whitelists to avoid false positives ! NAXSI doesn't relies on signatures, so it should be capable of defeating complex/unknown/obfuscated attack patterns. |--{ How does it work }------------------------------------------------------| NAXSI relies on two separate configuration parts. * Core Rules : Located at HTTP server level configuration. * WhiteLists & Specific Rules : Located at the HTTP location level configuration. The first one is what we called 'core rules'. It's a set of rules that will contain all characters or regular expression that will increase the score of the request, for exemple : MainRule "rx:<|>" "msg:html tag ?" "mz:ARGS|URL|BODY" "s:$XSS:8" id:1302; This rule, will match on both < and > characters (rx:<|>), and will increase the score associated to the XSS threat (s:$XSS:8). This pattern will be matched against various zones of the request : ARGS (GET arguments), URL (the full URI), and BODY (POST arguments). Each rule is associated to unique ID (here, 1302), that is used for whitelisting. There is not "many" core rules (34 at the time of writting), and this set should normally not evolve. On the other hand, we have a "local" configuration, which is to be defined "per site" (as NAXSI main goal is to work with NGINX as a RP), and which will define "how strict" the security policy of the site will be, as well as putting exceptions (whitelists) according the site specificities : -----------------------------------8<------------------------------------------- # Define to which "location" the user will be redirected when a request is # denied. DeniedUrl "/RequestDenied"; # Whitelist '|', as it's used on the /report/ page, in argument 'd' BasicRule wl:1005 "mz:$URL:/report/|$ARGS_VAR:d"; # Whitelist ',' on URL zone as it's massively used for URL rewritting ! BasicRule wl:1008 "mz:URL"; # Check rules CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; -----------------------------------8<------------------------------------------- Let's see this configuration again to clarify things : * 'BasicRule' : This directive is used here to whitelist some rules. As you can see (and expect !) you can be more or less laxist. For exemple, there is a directive (BasicRule wl:1008 ...) that will totally disable the rule 1008 checking against URL. This rule is normally matching the ',' character. On the other hand, you can make extremly precise rules, as this one : BasicRule wl:1005 "mz:$URL:/report/|$ARGS_VAR:d"; In the last exemple, we are whitelisting a rule, but only on one specific argument of one specific webpage (the argument named 'd' of the page '/report/'). As stated earlier, "local" configuration is also used to decide how "strict" one site (or one part of one site) will be, that is, what is the maximal tolerated page score before a request is denied : CheckRule "$SQL >= 8" BLOCK; This directive tells NAXSI that every request that has a 'SQL' score superior or equal to 8 will be denied. |--{ Denied requests and whitelist generation }------------------------------| When a user's request is denied, he will be (internally) redirected to the page defined in the configuration : DeniedUrl "/RequestDenied"; This page will receive detailed informations on the rules that matched (it's logued in log files too), as well as both the request and the user context, without giving information to the user (thanks to nginx internal redirects). Actually the page at /RequestDenied will be called with arguments, like : server=xxxx&uri=/vulnerable.php&ip=127.0.0.1&zone0=ARGS&id0=1010&var_name0=foo1&zone1=ARGS&id1=1011&... If you have a closer look to the URL above, you will understand that the following information will be transmitted to the forbidden page : - Which rule matched on which argument, in which part of the request (ARGS, BODY, URL, HEADERS ...) - Original URL and hostname - Client IP adress Those information are given for both statistics generation purpose, as well as for whitelist generation purposes. Yes, actually that's a very important aspect of NAXSI : As it is heavily relying on whitelist for configuration, the easiest the whitelist generation is , the easier the configuration is ! The thing being that the DeniedUrl page receives enough information to generate a set of whitelisting rules that will allow the request that was blocked to be allowed in the future. To make it clear : you can generate the whole NAXSI configuration, only by naviguating to the site, or even better, by using a clever crawler ! When you will do a navigation session on the website you want to create rules for, if you are in "LearningMode", some requests might be (and will be) tagued as blocked. They should be blocked, because, for example, the developpers (damn!) decided to massively use '|' for URL rewritting, and NAXSI will dislike this. So, as you are in learning mode (but the idea is the same when LearningMode is disabled), NAXSI will log the fact that the request was blocked because of the presence of multiples '|' in the URL. Then, with a simple script (a python script is provided), you can parse the log files and generate the appropriate whiterules to allow legitimate (false positive) requests. The more tricky part when talking about NAXSI and its whitelist, is when we come to sites that allows a LOT of wide user input : Comments, Registration forms and things like this. For this, either a carefull navigation, submitting real content is required (so that NAXSI will trigger every plausible rule on each kind of form field) or the usage of a clever crawler is required. In the worst, case, it will require to do a real navigation session to generate the appropriate whitelists. |--{ Statistics, Reporting and so on ... }-----------------------------------| This is a crucial part of any WAF, and this is not done yet ! But the good point is that, thanks to the principle of calling an external page that receives all the informations about every denied request, it is fairly simple to write your custom <insert your favorite language here> webpage to take care of the statistics / reporting. |--{ 3 .. 2 .. 1 .. practice ! }---------------------------------------------| Ok, now, let's have a look at the practice ! Let's admit we want to create a setup for a website. I won't cover the basics of setting up nginx as a reverse proxy, but rather focus on NAXSI configuration. If you have a "normal" web site, with no fancy URL rewritting or strange things, the default configuration should do the work, but let's have a look at website with fancy rewritting, and complex user forms. To make things easier, a good point is that we can 'fool' nginx and the OS into thinking that he is already the reverse proxy for the website, so that we can setup the configuration without any risk of impacting the production servers, so here we go : /etc/nginx/site-enabled/default: -----------------------------------8<------------------------------------------- server { proxy_set_header Proxy-Connection ""; resolver X.Y.Z; listen *:80; access_log /tmp/nginx_access.log; error_log /tmp/nginx_error.log debug; location / { # specific site config LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; ## check rules CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; # /specific site config proxy_pass http://xx.xx.xx.xx; proxy_set_header Host "www.xxx.com"; } location /RequestDenied { proxy_pass http://127.0.0.1:4242/denied_page.php; } } -----------------------------------8<------------------------------------------- Our configuration file is extremly similar to any nginx configuration, except: - We defined a NAXSI "per site" configuration. This is what will determine how NAXSI will behave for this site. - We define a location that will be used to redirect fobidden pages. Here, I have an apache instance listening on lo:4242 The NAXSI "per location" configuration, simply defines : - CheckRule : The maximum score for each kind of "threat" - The "LearningMode" directive is here to make the learning easier. By default, NAXSI will stop processing a request as soon as it hits one of the 'BLOCK' scores. With this directive, it will go through every rules, making whitelist generation easier, while allowing the request to pass, even if tagued as "BLOCKED", to make learning easier. - The 'SecRulesEnabled' directives tells that NAXSI should be activated for this location. In this way, you can decide to active / desactivate it easily for location X or Y. (For exemple, you might not want a WAF on your back-office ?) - DeniedUrl : We tell NAXSI were to redirect the user when a request is blocked. and we need to add this line in the http section of nginx.conf : --------------------------------8<-------------------------------------- include /etc/nginx/sec-rules/core.rules; --------------------------------8<-------------------------------------- The "core.rules" file is provided with NAXSI, and contains all the "patterns". So, here we go ! Let's start We can now fool the OS into thinking that xxx.com is on 127.0.0.1, edit /etc/hosts. We are ready for configuration ! Fire up your favorite browser at xxx.com and start navigation. <roll roll> As you should be in "learningmode", NAXSI will allow all the request, even if they reach a blocking score. As well as letting them pass, it will, as well, "forward" the request (like nginx's post_action directive) to your DeniedUrl, as well as the original blocked URL (in headers) and the generated blocking details. In this way, the web backend is abble to generate the white-lists, and reload nginx 'on the fly' with the new generated whitelist rules ;) The 'web' part is not written yet (I suck at html), but you can yet proceed in a different way : In your nginx's log file (if set as debug), you will see a lot of lines like this one appear : -----------------------------------8<------------------------------------------- 2011/07/11 17:12:27 [debug] 18653#0: *7 NAXSI_FMT: server=&uri=/skin/frontend/default/xxx/images/interface/fleche-grise.gif&ip=127.0.0.1&zone0=HEADERS&id0=1005&var_name0=cookie&zone1=HEADERS&id1=1008&var_name1=cookie&zone2=HEADERS&id2=1009&var_name2=cookie&zone3=HEADERS&id3=1010&var_name3=cookie&zone4=HEADERS&id4=1011&var_name4=cookie&zone5=HEADERS&id5=1308&var_name5=cookie&zone6=HEADERS&id6=1309&var_name6=cookie&zone7=HEADERS&id7=1313&var_name7=cookie -----------------------------------8<------------------------------------------- So, once you think you've done a reasonable crawling on your site, you can launch the "rules generator" [destination rules file] [nginx's log file]: -----------------------------------8<------------------------------------------- bui@zeroed:~$ ./rules_generator.py RANGE for ID=1000,ZONE=URL, range=0-4 # for rule 1000, we have 4 elements in zone URL #duplicate for id 1000, delete (4 elems) RANGE for ID=1002,ZONE=URL, range=1-89 ... {'id': '1313', 'uri': '', 'var_name': 'cookie', 'zone': 'HEADERS'}] -----------------------------------8<------------------------------------------- Rules generator default output file is /tmp/RT_naxsi.tmp, and looks like : -----------------------------------8<------------------------------------------- bui@zeroed:/home/bui/secdev/nginx/web: cat /tmp/RT_naxsi.tmp | grep ^Basic BasicRule wl:1000 "mz:$URL:/skin/frontend/default/lepape/images/titre_notre_selection2.gif|URL"; BasicRule wl:1002 "mz:URL"; BasicRule wl:1008 "mz:URL"; BasicRule wl:1005 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1008 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1009 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1010 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1011 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1308 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1309 "mz:$HEADERS_VAR:cookie"; BasicRule wl:1313 "mz:$HEADERS_VAR:cookie"; -----------------------------------8<------------------------------------------- What happened just here ? The Rule Generator parsed nginx log file, extracted all the "denied urls", and generated (and then factorized) whitelist rules from your session ! If you did an exhaustive enough crawling / browsing session, your ruleset might be complete enough and ready for production ! |--{ Detail configuration }--------------------------------------------------| Here, I will go through all the directives supported by NAXSI. |--{ Location configuration }------------------------------------------------| LearningMode : By default, NAXSI will stop parsing a request as soon as it reaches a score that will block the request. When LearningMode is enabled, the request will be matched against every possible rules. This directive should be used when configuring NAXSI ONLY (a.k.a : generating whitelists) SecRulesEnabled : If the directive is not present, NAXSI will not inspect anything. SecRulesDisabled : If the directive is present, NAXSI will not inspect anything. (The two rules above are here for easier usage in production environnment, when you want to be abble to simply enable / disable the module) DeniedUrl "/location" : This directive is used to tell NAXSI where the user should be redirected when a request is blocked. The location should be present in NGINX configuration for this to work, as we are relying on nginx's internal redirect feature. BasicRule : The 'main' thing you should care about. This can be used, either to add some whitelist, or to create some specific rules for a location (might be usefull if you have super crappy websites). Here are some examples of possible BasicRule syntaxes : - BasicRule wl:1005 "mz:$URL:/bar/|$ARGS_VAR:foo" : Whitelist rule 1005 on the GET "foo" arg of page /bar/ - BasicRule wl:1005 "mz:$URL:/bar/|ARGS" : Whitelist rule 1005 on the every GET arg of page /bar/ - BasicRule wl:1005 : Globally disable rule 1005 on the location - BasicRule wl:1005 "mz:HEADERS" : Whitelist rule 1005 for all HEADERS - mz supports keywords like ARGS (global GET args), BODY (global POST args), URL (url, rly), $ARGS_VAR (named GET arg), $BODY_VAR (named POST arg), HEADERS (HTTP headers), $HEADERS_VAR (named header var) CheckRule : Used to determine when the request should be blocked, for example : CheckRule "$SQL >= 8" BLOCK : If the $SQL score is superior or equal to 8, the request will be blocked. |--{ Main configuration }----------------------------------------------------| In main configuration, a.k.a core rules, there is only one directive : MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS" "s:$SQL:8" id:1003; This specific rule for example, will be matched against GET,POST arguments, as well as the URL. If the '/*' string is found, the $SQL score will be increased by 8. Sounds pretty simple right ? But you can as well do some tricky things ! MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADER_VAR:Content-type" "s:$SQL:42" id:1999; This one is a negative rule (means that the score will be applied if the rule DOES NOT match). This one is used to prevent strange content types on POSTs. To litteraly translate it, it means : If a "Content-type" HTTP header is present, check weither it's 'application/x-www-form-urlencoded' or 'multipart/form-data' (you noticed the rx: keyword, yes it means that it's a regular expression). If the content-type is different, then increase $SQL score by 42.
About
NAXSI is an open-source, high performance, low rules maintenance WAF for NGINX
naxsi.googlecode.com github.com/nbs-system/naxsi
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published