A Web Application Firewall for Nginx

A web application firewall (WAF) is an application that monitors network traffic at the application layer.

OSI (Open Systems Interconnection) is one of the most referenced network traffic frameworks across internet related discussions. When a package crosses Layer 6 (Presentation) and moves towards Layer 7 (Application) it undergoes decrypting/decoding operations. Each of these operations can be susceptible to faulty decoding and interpretation that can be used to break out of the standard application context. Injections are just one type of such vulnerabilities and for a long time have been the number one cause of concern especially since traditional IDS/IPS appliances cannot handle these threats.

About ModSecurity

ModSecurity was historically the web application firewall engine itself. It is compatible with Apache, IIS, and Nginx and has been maintained by a third-party company. The firewall cross references a list of rules to a stream of HTTP headers provided by a webserver/proxy. As of now this repository was simplified and contains only the main library LibModSecurity. The library itself can be called from your own server implementation directly or via wrappers specific to individual programming languages.

The parent company’s support is scheduled to end on July 1 2024 at which time the project is supposed to be maintained by the open-source community.

Install the Nginx connector

The Nginx connector is an Nginx dynamic module and it can be installed via the Fedora package nginx-mod-modsecurity. It has libmodsecurity.so as a dependency so for this use-case this package is the firewall itself.

[user@fedora ~]$ sudo dnf install -y nginx nginx-mod-modsecurity 
[user@fedora ~]$ rpm -qR nginx-mod-modsecurity
config(nginx-mod-modsecurity) = 1.0.3-3.fc38
libc.so.6(GLIBC_2.4)(64bit)
libmodsecurity.so.3()(64bit)
nginx(abi) = 1.24.0
nginx-filesystem
...

Once installed, you will see that the connector adds a few important files to /etc/nginx.

[user@fedora ~]$ rpm -ql nginx-mod-modsecurity 
/etc/nginx/modsecurity.conf                   # waf config
/etc/nginx/nginx.conf.modsecurity             # nginx sample conf
/usr/lib64/nginx/modules/ngx_http_modsecurity_module.so   
/usr/share/nginx/modules/mod-modsecurity.conf
/usr/share/doc/nginx-mod-modsecurity/README.md  
...

The connector extends Nginx by providing some extra configuration directives. The following sections will demonstrate a few of the example directives in the nginx.conf.modsecurity file. A complete list of the directives can be found in the README.md file or on the project’s GitHub page.

Enable the web application firewall

nginx.conf.modsecurity is the Nginx configuration we are going to run. Uncomment the modsec* lines as shown below.

[user@fedora ~]$ sudo sed -i 's/#modsec/modsec/g' /etc/nginx/nginx.conf.modsecurity
[user@fedora ~]$ grep -C2 modsecurity /etc/nginx/nginx.conf.modsecurity
        # Enable ModSecurity WAF, if need
        modsecurity on;
        # Load ModSecurity CRS, if need
        modsecurity_rules_file /etc/nginx/modsecurity.conf;

Start the server inside the shell and observe the logs to make sure the seven default rules defined in modsecurity.conf are loaded.

[user@fedora ~]$ sudo nginx -c /etc/nginx/nginx.conf.modsecurity
[user@fedora ~]$ head /var/log/nginx/error.log
2023/10/21 23:55:09 [notice] 46218#46218: ModSecurity-nginx v1.0.3 (rules loaded inline/local/remote: 0/7/0)
2023/10/21 23:55:09 [notice] 46218#46218: using the "epoll" event method
2023/10/21 23:55:09 [notice] 46218#46218: nginx/1.24.0
2023/10/21 23:55:09 [notice] 46218#46218: OS: Linux 6.5.7-200.fc38.x86_64

Test the default rules by sending some data that does not respect the content-type header format.

[user@fedora ~]$ curl -X POST http://localhost -H "Content-Type: application/json" --data ""
[user@fedora ~]$ tail /var/log/modsec_audit.log
...
---rH5bFain---H--
ModSecurity: Warning. Matched "Operator `Eq' with parameter `0' against variable `REQBODY_ERROR' (Value: `1' ) [file "/etc/nginx/modsecurity.conf"] [line "75"] [id "200002"] [rev ""] [msg "Failed to parse request body."] [data "JSON parsing error: lexical error: invalid char in json text.x0a"] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "10.0.2.100"] [uri "/"] [unique_id "169795900388.487044"] [ref "v121,1"]

Extend your web application firewall with the OWASP core rule set

The default Nginx connector comes with seven rules. The OWASP Core Rule Set v3.3.5 is more extensive and covers many scenarios.

Copy the archive and extract the rules.

[user@fedora ~]$ curl -fSL https://github.com/coreruleset/coreruleset/archive/refs/tags/v3.3.5.tar.gz --output /tmp/v3.3.5.tar.gz
[user@fedora ~]$ sudo tar -C /etc/nginx -xvf /tmp/v3.3.5.tar.gz
[user@fedora ~]$ tree -L 1 /etc/nginx/
/etc/nginx/
├── conf.d
├── default.d
├── modsecurity.conf          # waf config
├── nginx.conf                        
├── nginx.conf.modsecurity    # nginx waf enabled
├── coreruleset-3.3.5                 
├   ├── rules                 # rules directory
├       ...
├   ...

You now have a rules directory within the nginx configuration folder with all the current OWASP rules. Next, make Nginx aware of these rules. The following instructions originate from the OWASP ./INSTALL file.

Create a crs.conf file and include all the relevant config files in the global web application firewall config file (modsecurity.conf).

[user@fedora ~]$ sudo cp /etc/nginx/coreruleset-3.3.5/crs-setup.conf.example /etc/nginx/coreruleset-3.3.5/crs.conf
[user@fedora ~]$ echo -e "nInclude /etc/nginx/coreruleset-3.3.5/crs.conf"  | sudo tee -a /etc/nginx/modsecurity.conf
[user@fedora ~]$ echo -e "nInclude /etc/nginx/coreruleset-3.3.5/rules/*.conf" | sudo tee -a /etc/nginx/modsecurity.conf
[user@fedora ~]$ tail /etc/nginx/modsecurity.conf 
Include /etc/nginx/coreruleset-3.3.5/crs.conf 
Include /etc/nginx/coreruleset-3.3.5/rules/*.conf 

According to docs, the order of including these files is important. The tee command shown above has placed the new Include lines at the end of the modsecurity.conf file. Now, reload Nginx with this new configuration.

[user@fedora ~]$ sudo nginx -s stop && sudo nginx -c /etc/nginx/nginx.conf.modsecurity
[user@fedora ~]$ tail /var/log/nginx/error.log
2023/10/22 10:53:23 [notice] 202#202: exit
2023/10/22 10:53:50 [notice] 230#230: ModSecurity-nginx v1.0.3 (rules loaded inline/local/remote: 0/921/0)
2023/10/22 10:53:50 [notice] 230#230: using the "epoll" event method
2023/10/22 10:53:50 [notice] 230#230: nginx/1.24.0
2023/10/22 10:53:50 [notice] 230#230: OS: Linux 6.5.7-200.fc38.x86_64
2023/10/22 10:53:50 [notice] 230#230: getrlimit(RLIMIT_NOFILE): 524288:524288
2023/10/22 10:53:50 [notice] 231#231: start worker processes

Notice Nginx loaded 921 rules successfully. Some tests are also needed to make sure the rules are actually checked by the web application firewall. Here again, we reference the snippet Testing the Installation from the ./INSTALL file.

[user@fedora ~]$ curl 'http://localhost/?param=''>'
[user@fedora ~]$ tail /var/log/modsec_audit.log 
...
---8NSpdnLe---H--
ModSecurity: Warning. detected XSS using libinjection. [file "/etc/nginx/coreruleset-3.3.5/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "38"] [id "941100"] [rev ""] [msg "XSS Attack Detected via libinjection"] [data "Matched Data: XSS data found within ARGS:param: >"] [severity "2"] [ver "OWASP_CRS/3.3.5"]
...

Conclusions

How to configure a web application firewall for an Nginx server has been demonstrated. This deployment uses standard rules plus the OWASP Core Rule Set v3.3.5. The firewall demonstrated above is running in detection mode and logging unusual actions. Running the firewall in prevention mode requires further changes to modsecurity.conf. Refer to ModSecurity Reference Manual v3.x for instructions on how to enable prevention mode and much more.

All the best.

fedora Fedora Updates