Enforcing Security in Web App Firewalls using Wazuh Active Response

5 min readMay 23, 2022


Web Application Firewalls (WAF) filter, monitor, and block HTTP traffic to a web application. They use a combination of rule-based logic and signatures to detect and prevent attacks.

WAF deployment options include:

  • Network-based WAF (generally a hardware-based appliance).
  • Host-based WAF (fully integrated into an application’s software — Apache, NGINX or IIS).
  • Cloud-based WAFs, such as Cloudflare.

In this entry we cover how to deploy a WAF using OpenSource technologies and how to enforce additional security leveraging Wazuh EDR and active response

Build your own WAF.

WAF = Apache Reverse Proxy + ModSec + OWASP CRS

WAF Solution:

  • OS: FreeBSD.
  • Apache Web Server working as a reverse proxy.
  • GeoIP integrated in Apache.
  • Anti Malbots and Bad Referrers integrated in Apache.
  • Apache ModSecurity Module: Security Engine.
  • OWASP Core Rule Set (CRS): Rule Logic and signatures.

Why FreeBSD: High performance TCP/IP stack. Choose your own OS, if not familiar with BSD.

Apache + ModSecurity: Better performance than NGINX + ModSecurity. On top of that, Apache offers better logging capabilities too.

OWASP Core Rule Set (CRS): Used by most commercial WAFs:

  • Azure WAF.
  • AWS WAF.
  • Cloudflare.
  • Google Cloud Armor.
  • Trustwave.
  • Fortinet WAF.
  • Citrix WAF.
  • ……

GeoIP in Apache

Install GeoIP package and the module for Apache

pkg install geoipupdate
pkg install ap24-mod_maxminddb

Edit /usr/local/etc/GeoIP.conf and add account ID and License key:

# GeoIP.conf file for `geoipupdate` program, for versions >= 3.1.1.# Used to update GeoIP databases from https://www.maxmind.com.# For more information about this config file, visit the docs at# https://dev.maxmind.com/geoip/geoipupdate/.# `AccountID` is from your MaxMind account.AccountID account_id# `LicenseKey` is from your MaxMind accountLicenseKey key# `EditionIDs` is from your MaxMind account.EditionIDs GeoLite2-ASN GeoLite2-City GeoLite2-Country

Download GeoIP databases (execute command below):


After execution the GoIP databases will be downloaded into /usr/local/share/GeoIP:

ls -lrt /usr/local/share/GeoIP/total 62058-rw-------  1 root  wheel         0 Dec  2 02:56 .geoipupdate.lock-rw-r--r--  1 root  wheel   7537328 Dec  2 02:56 GeoLite2-ASN.mmdb-rw-r--r--  1 root  wheel  73640335 Dec  2 02:56 GeoLite2-City.mmdb-rw-r--r--  1 root  wheel   6618745 Dec  2 02:56 GeoLite2-Country.mmdb

Edit /usr/local/etc/apache24/httpd.conf and add:

# Third party modules### maxminddb module addLoadModule maxminddb_module   libexec/apache24//mod_maxminddb.so<IfModule maxminddb_module>MaxMindDBEnable OnMaxMindDBFile CITY_DB /usr/local/share/GeoIP/GeoLite2-City.mmdbMaxMindDBFile COUNTRY_DB /usr/local/share/GeoIP/GeoLite2-Country.mmdbMaxMindDBEnv MM_CONTINENT_CODE          COUNTRY_DB/continent/codeMaxMindDBEnv MM_CONTINENT_NAME          COUNTRY_DB/continent/names/enMaxMindDBEnv MM_COUNTRY_CODE            COUNTRY_DB/country/iso_codeMaxMindDBEnv MM_COUNTRY_NAME            COUNTRY_DB/country/names/en</IfModule>

To enable GeoIP, the module and blocking logic needs to be configured at vhost level. For Apache working as a reverse proxy, the settings need to be under the <Proxy> or <Location>sections.

GeoIP config for “deny by default, allow by exception”:

<Proxy *>SetEnvIf MM_COUNTRY_CODE COUNTRY_CODE_1 AllowedCountrySetEnvIf MM_COUNTRY_CODE COUNTRY_CODE_2 AllowedCountryorder deny,allowdeny from allallow from env=AllowedCountry</Proxy>

AntiBots / Bad Referrers

This GitHub repo includes instructions and required files to enable blocking “bad bots” and “bad referrers” from sending web requests.

Apache Reverse Proxy + ModSec + OWASP CRS


Install ModSec module for Apache:

pkg install ap24-mod_security

(ModSec settings will be completed after downloading and enabling OWASP CRS)

OWASP Core Rule Set

OWASP CRS provides protection against many common attack categories.
SQL Injection, Cross Site Scripting, Local File Inclusion, etc.

The paranoia Level (1–4) determines the number of rules to load/apply.
Rules are applied to requests and responses (information leak prevention).

The anomaly score system is used to set a threshold for cumulated number of rule violations. Suspicious request above threshold will be blocked.

Downloading and enabling CRS in ModSec:

cd /usr/local/etc/modsecuritygit clone https://github.com/coreruleset/coreruleset.gitcd corerulesetcp crs-setup.conf.example /usr/local/etc/modsecurity/crs-setup.conf

Edit file /usr/local/etc/apache24/modules.d/280_mod_security.conf:

LoadModule unique_id_module libexec/apache24/mod_unique_id.soLoadModule security2_module libexec/apache24/mod_security2.soInclude /usr/local/etc/modsecurity/*.confInclude /usr/local/etc/modsecurity/coreruleset/rules/*.conf

Edit /usr/local/etc/modsecurity/modsecurity.conf and enable the rule engine; also recommended to increase PCRE limit (regex):

SecRuleEngine OnSecDebugLogLevel 0SecPcreMatchLimit 150000SecPcreMatchLimitRecursion 150000

A paranoia level of 2 is a good balance for security while trying to keep false positives to a minimum:

Edit /usr/local/etc/modsecurity/crs-setup.conf:

SecAction \"id:900000,\phase:1,\nolog,\pass,\t:none,\setvar:tx.paranoia_level=2

In the same file, modify the default action:

SecDefaultAction "phase:1,log,auditlog,deny"
SecDefaultAction "phase:2,log,auditlog,deny"

Rules 920350 (hostname in HTTP header is an IP address), 920280 and rule 920290 (Missing/Empty Host Header) and 920330 (Empty User-Agent Header) can be changed to a deny action (edit file /usr/local/etc/modsecurity/coreruleset/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf):

SecRule REQUEST_HEADERS:Host "@rx (?:^([\d.]+|\[[\da-f:]+\]|[\da-f:]+)(:[\d]+)?$)" \"id:920350,\phase:1,\deny,\t:none,\msg:'Host header is a numeric IP address',\logdata:'%{MATCHED_VAR}',\tag:'application-multi',\tag:'language-multi',\tag:'platform-multi',\tag:'attack-protocol',\tag:'paranoia-level/1',\tag:'OWASP_CRS',\tag:'capec/1000/210/272',\tag:'PCI/6.5.10',\ver:'OWASP_CRS/3.4.0-dev',\severity:'WARNING',\setvar:'tx.anomaly_score_pl1=+%{tx.warning_anomaly_score}'"

Wazuh Active Response + PF in action.

Kernel Filter for Blocked Requests.

How WAFs block HTTP requests:
Request not forwarded to the backend server.
HTTP Status = 403 — Forbidden sent to client (default).

Web scanners/crawlers/automation tools will keep sending requests.

Kill switch: Block traffic from repeated offenders using Packet Filters:
Inspect WAF activity via Apache Logs.
Translate WAF blocking event into a kernel packet filter rule.
Additionally, detect web scans via event correlation and apply filter rule as well.

Leveraging Wazuh’s active response we can activate BSD’s Packet Filter (pF) to dynamically block source IPs at kernel level for web requests blocked by ModSecurity.

The active response script “pf.sh” relies on a pF table named “ossec_fwtable”. We’ll enable pF on the WAF and enable this table.

Create the file “/etc/pf.conf” with the following configuration:

table <ossec_fwtable> persist #ossec_fwtableblock in on vtnet0 from <ossec_fwtable>

Where vtnet0 is the public interface on the WAF. Change as per your VM settings.

Enable pF as a service by adding the following lines to “/etc/rc.conf”:


Start pF service:

service pf start

Wazuh Manager configuration to enable the active response:

<command><name>pf_block_ip</name><executable>pf.sh</executable><expect>srcip</expect><timeout_allowed>yes</timeout_allowed></command>----------------------------------------------<!-- WAF Active Response --><active-response><disabled>no</disabled><command>pf_block_ip</command><location>local</location><timeout>600</timeout><rules_id>30411</rules_id></active-response>

The AR is triggered by the rule with ID 30411 (“ModSecurity rejected a query”). After 10 mins, the source IP that got blocked will be removed from the pF table.

Adding “frequency” we can correlate several ModSec blocks from same IP before triggering the Active response.

With all the logic above applied, several events = “ModSecurity rejected a query” within the interval defined in the ruleset will trigger a correlation event of level 12 which, in turn, will activate the pF add in the WAF, blocking the SRC IP of the repeated offender:

Wazuh Active Response after ModSec Events

Need Help?

The functionality discussed in this post, and so much more, are available via the SOCFortress platform. Let SOCFortress help you and your team keep your infrastructure secure.

Website: https://www.socfortress.co/

Platform Demo: https://www.socfortress.co/demo_access.html




SOCFortress is a SaaS company that unifies Observability, Security Monitoring, Threat Intelligence and Security Orchestration, Automation, and Response (SOAR).