Working on a WAF solution for the Nginx ecosystem provides a lot of opportunities for discussion, given that such work is a meeting of crossroads. Mixing high-performance engineering, WAF technologies, ModSecurity DSL, and the OpenResty community puts lua-resty-waf in a unique context. I often get asked about other WAF solutions for Nginx, including Naxsi, and how these solutions compare to ModSecurity, lua-resty-waf (and other security OpenResty libs), and commercial solutions. I haven’t spent a lot of time working with the Naxsi project, but I’ve poked at it enough to at least start putting some thoughts on paper.
Right off the bat- Naxsi and ModSecurity (and thus, lua-resty-waf) are apples and oranges. Naxsi is designed specifically to protect against a small field of threats (XSS and SQLi – it’s in the name). Out of the box it provides an aggressive negative security model, defining a large blanket of suspicious behaviors (really, just the existence of essentially some non-alphanumeric chars in request content) and placing the onus of responsibility on the administrator to manage false positives. Beyond this, it provides some learning tooling to generate exceptions against known good traffic, easing the burden of work for sysadmins.
Not unlike ModSecurity, Naxsi executes against a set of rules using a custom DSL, lexed and parsed at master start. It’s rule syntax is even similar to ModSecurity’s: consider the following example from the distributed core rules:
MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;
Without even digging into the documentation, we can make some assumptions about what’s going on here.
rx:select|... defines a regular expression against which to match some content, defined later in the rule (pretty clear to see- we even see multiple types of request data inspected using the pipe character, just as SecRules defines). We can also easily spot a context message and numeric ID. The second-to-last token is a bit more cryptic; it defines a value to be applied to an increment variable used to assign anomalous value to the request. This model mimics anomaly scoring pattern found in numerous other security applications, from legacy spam filters to the OWASP CRS (though in the latter case, OWASP CRS uses ModSecurity TX variable storage as an implementation vehicle to build a scoring model, though this functionality is not built into ModSecurity itself natively; Naxsi bakes this concept into the core of its engine).
My biggest (negative) reaction to the project comes from this line in the readme:
Contrary to most Web Application Firewalls, Naxsi doesn’t rely on a signature base like an antivirus, and thus cannot be circumvented by an “unknown” attack pattern.
This bold claim seems to imply that Naxsi installations are impervious to zero-day threats, or rather, than sites protected by Naxsi are impervious to zero-day threats. This feels… overly aggressive, and while I have done no examination into the validity of this claim, it does feel very far-fetched. We also need to remember that Naxsi specifically targets a small subset of modern web app vulnerabilities (XSS, SQLI, R/LFI), so it would disingenuous to comment on zero-day exploits falling outside of these categories. Still, this essentially boils down to “we drop requests that contain brackets, parens, double dots, and quotes, therefore XSS and SQLi will never be a thing”.
And to be clear, the claim that Naxsi doesn’t rely on signatures simply isn’t true- it’s whitelist model is entirely signature based (and yes, there’s a note on this project indicating that these rules aren’t supported by the Naxsi team, but the project is linked from the official Naxsi GH page). The only difference here is that signatures apply to exceptions, not positive matches. It’s fairly clear that the intent behind Naxsi’s claim is to highlight its negative model, so while we’re starting to get into some pedantry, it’s definitely worth remembering that, as a data-driven model, Nasxi cannot work without signatures.
The readme also indicates that the rules distributed with the project “cover 99% of known patterns involved in website vulnerabilities”. I would very much like to see the data supporting this claim (snarky comments aside, that would certainly be an interesting research project).
Naxsi’s feature scope also feels limited in the context of modern web apps:
- Very limited operator set- Naxsi rules are restricted to PCRE regex and a custom implementation of strstr (plus client9’s excellent libinjection library)
- No ability to inspect response headers or body
- No persistent data storage
- No anti-evasion measures
- DSL is not Turing complete
- No audit logging (no way to know why a request was blocked)
- Limited ability to handle large request bodies (as well as very limited handling of malformed multipart requests)
And yes, I base this list largely off features available in other FOSS WAFs (I talked about this a bit last year at the 2016 Lua User’s Workshop).
Beyond this, I find (admittedly pedantic) issues with small chunks of source (and I’m not the only one). Naxsi uses a mix of prefixes among strucs and functions; the use of
ngx_http_ is particularly confusing, especially to new developers. This namespace pollution is wholly unnecessary. There are also a number of compile-time definitions that feel like they should be configurable (at least as an advanced option), including recursive JSON parsing depth and PCRE ovec sizes, and overall the codebase is littered with inconsistent style. It feels very much like an odd, organic blend of data-driven architecture and hard-coded logic branches based on rule definitions, making extension and development very difficult.
It’s also very discouraging to see a three year old licensing conflict.
To be clear, I’m not trying to dump all over Naxsi’s parade. Pairing an aggressive negative security model with an automated, behavior-based whitelist approach is not particularly novel, but the amount of work required to implement this is not trivial, so seeing it in a FOSS project is pretty cool (and I’m definitely a fan of this mentality where appropriate). But its feature set is small, and doesn’t hold up to the demands of modern application security needs, particularly in a cloud/reverse proxy environment that Nginx’s high performance would warrant. I very much look forward to watching the continued development of the project.