See what’s actually being exploited right now.

Discover Live Exploit Tracker
crowdsec and owasp

Protecting Your Web Applications with OWASP CRS and CrowdSec

If you’re already running CrowdSec’s AppSec component with virtual patching rules, you’re well protected against known CVEs. But what about the attacks that don’t target a specific vulnerability: the SQL injections, cross-site scripting attempts, and command injections that probe your applications every day?

That’s where the OWASP Core Rule Set (CRS) comes in. CrowdSec supports CRS natively through the Coraza WAF engine, giving you broad attack pattern detection alongside your existing virtual patching setup.

In this post, we’ll walk through what CRS offers, how to deploy it, and how to tune it for your applications.

What is OWASP Core Rule Set (CRS)?

The OWASP Core Rule Set is among the most widely deployed sets of WAF rules worldwide. Actively maintained by the OWASP community, it provides generic attack detection against:

  • SQL injection
  • Cross-site scripting
  • Command injection
  • Path traversal
  • And more

Unlike virtual patching, which targets specific known CVEs, the CRS uses a more generic approach to detect exploitation of still-unknown vulnerabilities.

This comes at a cost: a higher chance of false positives depending on the application.

Using both CRS and virtual patching rules is complementary: virtual patching rules for precise targeting of known vulnerabilities without the need for any configuration, and CRS to catch everything else.

Using the CRS with CrowdSec

CrowdSec offers two deployment modes for CRS: out-of-band or in-band.

Out-of-band (non-blocking)

In this mode, CRS rules analyze your traffic asynchronously. No request is ever blocked by a single rule match. Instead, events are generated and fed into CrowdSec’s scenario engine. If an IP triggers too many different rule violations in a short timespan, it gets banned.

This is the recommended starting point because it lets you:

  • See what CRS detects in your traffic without disrupting legitimate users
  • Identify false positives and tune the CRS accordingly

In-band (blocking)

In blocking mode, requests that reach the CRS threshold for blocking are dropped immediately. Alerts are generated by default, giving you visibility into what is blocked.

Getting Started: Installation

Prerequisites

You need a working CrowdSec setup with a WAF-capable remediation component. This includes the Nginx/OpenResty bouncer, Traefik bouncer, HAProxy bouncer, or any other bouncer (also known as Remediation Component) that supports the AppSec protocol.

Step 1: Install the CRS collection

For non-blocking mode:

cscli collections install crowdsecurity/appsec-crs

This installs:

  • crowdsecurity/crs: the configuration that loads CRS rules in out-of-band mode
  • crowdsecurity/crowdsec-appsec-outofband: a scenario that bans IPs after 5+ out-of-band rule violations to offer a base level of protection, even if the rules themselves do not block anything directly.

Step 2: Configure the acquisition

Add the CRS config to your acquisition file (e.g., /etc/crowdsec/acquis.d/waf.yaml):

source: appsec
appsec-configs:
  - crowdsecurity/crs
labels:
  type: appsec

If you already have an acquisition file for AppSec with other configs (like virtual patching), simply add crowdsecurity/crs to the existing appsec-configs list.

Step 3: Enable per-match alerting

By default, non-blocking mode doesn’t generate an alert for every individual rule match. If you want visibility into each match (useful during the tuning phase), create /etc/crowdsec/appsec-configs/crs-alerting.yaml:

name: custom/crs-alerting
on_match:
  - filter: IsOutBand == true
    apply:
      - SendAlert()
      - CancelEvent()

The CancelEvent() directive is optional: if set, no event is generated for the scenario engine, meaning CrowdSec won’t take automated decisions based on these matches. This is useful if you want alerts for monitoring but don’t want any automated bans yet.

Then add it to your acquisition:

source: appsec
appsec-configs:
  - crowdsecurity/crs
  - custom/crs-alerting
labels:
  type: appsec

Even though an alert is generated, because the rules are loaded out-of-band, nothing will be blocked, but you will still know exactly what is matching.

Step 4: Restart CrowdSec

sudo systemctl restart crowdsec

Step 5: Confirm it’s working

Now, we can see if everything is working as it should. Let’s exploit a (fake) SQL injection:

$ curl "localhost/?a='+OR+'1'='1" -i
HTTP/1.1 200 OK
...

Our request was not blocked: it’s expected as the CRS is configured in non-blocking mode.

If we look at the list of alerts in crowdsec with cscli alerts list and cscli alerts inspectd -d, We can indeed see the request was detected by the CRS:

root@instance-20240401-2335:~# cscli alerts inspect -d 105901

################################################################################################

 - ID           : 105901
 - Date         : 2026-03-12T09:44:32Z
 - Machine      : 717704f5186c4314928c2060bf5169f32E1tgFLtep049JcD
 - Simulation   : false
 - Remediation  : false
 - Reason       : anomaly score out-of-band: sql_injection: 10, anomaly: 10,
 - Events Count : 4
 - Scope:Value  : Ip:127.0.0.1
 - Country      :
 - AS           :
 - Begin        : 2026-03-12T09:44:32Z
 - End          : 2026-03-12T09:44:32Z
 - UUID         : 8cf71673-81d7-4ba0-b211-0d415a0f51fd


 - Context  :
╭───────────────┬─────────────────────────────────────────────────────╮
│      Key      │                        Value                        │
├───────────────┼─────────────────────────────────────────────────────┤
│ ja4h          │ ge11nn020000_cf69e1861692_000000000000_000000000000 │
│ matched_zones │ ARGS.a                                              │
│ method        │ GET                                                 │
│ msg           │ SQL Injection Attack Detected via libinjection      │
│ name          │ native_rule:942100                                  │
│ target_uri    │ /?a='+OR+'1'='1                                     │
│ user_agent    │ curl/7.81.0                                         │
╰───────────────┴─────────────────────────────────────────────────────╯

 - Events  :

- Date: 2026-03-12 09:44:32 +0000 UTC
╭───────────────┬──────────────────────────╮
│      Key      │           Value          │
├───────────────┼──────────────────────────┤
│ data          │                          │
│ matched_zones │ REQBODY_PROCESSOR        │
│ message       │ Enabling body inspection │
│ rule_name     │ native_rule:901340       │
│ target_fqdn   │ localhost                │
│ uri           │ /?a='+OR+'1'='1          │
╰───────────────┴──────────────────────────╯

- Date: 2026-03-12 09:44:32 +0000 UTC
╭───────────────┬──────────────────────────────────────────────────────╮
│      Key      │                         Value                        │
├───────────────┼──────────────────────────────────────────────────────┤
│ data          │ Matched Data: s&sos found within ARGS:a: ' OR '1'='1 │
│ matched_zones │ ARGS.a,ARGS.a                                        │
│ message       │ SQL Injection Attack Detected via libinjection       │
│ rule_name     │ native_rule:942100                                   │
│ target_fqdn   │ localhost                                            │
│ uri           │ /?a='+OR+'1'='1                                      │
╰───────────────┴──────────────────────────────────────────────────────╯

- Date: 2026-03-12 09:44:32 +0000 UTC
╭───────────────┬──────────────────────────────────────────────────╮
│      Key      │                       Value                      │
├───────────────┼──────────────────────────────────────────────────┤
│ data          │                                                  │
│ matched_zones │ TX.blocking_inbound_anomaly_score                │
│ message       │ Inbound Anomaly Score Exceeded (Total Score: 10) │
│ rule_name     │ native_rule:949110                               │
│ target_fqdn   │ localhost                                        │
│ uri           │ /?a='+OR+'1'='1                                  │
╰───────────────┴──────────────────────────────────────────────────╯

- Date: 2026-03-12 09:44:32 +0000 UTC
╭───────────────┬──────────────────────────────────────────────────────────────╮
│      Key      │                             Value                            │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ data          │                                                              │
│ matched_zones │ UNKNOWN                                                      │
│ message       │ Anomaly Scores: (Inbound Scores: blocking=10, detection=10,  │
│               │ per_pl=10-0-0-0, threshold=5) - (Outbound Scores:            │
│               │ blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) -      │
│               │ (SQLI=10, XSS=0, RFI=0, LFI=0, RCE=0, PHPI=0, HTTP=0,        │
│               │ SESS=0, COMBINED_SCORE=10)                                   │
│ rule_name     │ native_rule:980170                                           │
│ target_fqdn   │ localhost                                                    │
│ uri           │ /?a='+OR+'1'='1                                              │
╰───────────────┴──────────────────────────────────────────────────────────────╯

A note about the reason given for the block: CRS works with a threshold system. Each rule that matches on the request will increase by some value a global score, and if the global score exceeds a preconfigured threshold (5 by default), the request will be blocked.

This is why the reason appears as anomaly score out-of-band: sql_injection: 10, anomaly: 10,.

Crowdsec automatically extracts the scoring information from the CRS to give you, at first glance, details about what was wrong with the request.

Switching to blocking mode

Once you’re confident in your CRS tuning, switch to blocking mode:

cscli collections install crowdsecurity/appsec-crs-inband

Update your acquisition:

source: appsec
appsec-configs:
  - crowdsecurity/crs-inband
labels:
  type: appsec

In this mode, any request that reaches the default CRS threshold is dropped, and alerts are generated automatically.

Let’s retry the same request as previously:

$ curl "localhost/?a='+OR+'1'='1" -i
HTTP/1.1 403 Forbidden
...

This time, the request actually got blocked!

Again, we can inspect the alert:

root@instance-20240401-2335:~# cscli alerts inspect -d 105902

################################################################################################

 - ID           : 105902
 - Date         : 2026-03-12T09:49:04Z
 - Machine      : 717704f5186c4314928c2060bf5169f32E1tgFLtep049JcD
 - Simulation   : false
 - Remediation  : false
 - Reason       : anomaly score block: sql_injection: 10, anomaly: 10,
 - Events Count : 4
 - Scope:Value  : Ip:127.0.0.1
 - Country      :
 - AS           :
 - Begin        : 2026-03-12T09:49:03Z
 - End          : 2026-03-12T09:49:03Z
 - UUID         : 7ef38529-5e7e-4d2e-9e21-24ec7a89a1ee


 - Context  :
╭───────────────┬─────────────────────────────────────────────────────╮
│      Key      │                        Value                        │
├───────────────┼─────────────────────────────────────────────────────┤
│ ja4h          │ ge11nn020000_cf69e1861692_000000000000_000000000000 │
│ matched_zones │ ARGS.a                                              │
│ method        │ GET                                                 │
│ msg           │ SQL Injection Attack Detected via libinjection      │
│ name          │ native_rule:942100                                  │
│ target_uri    │ /?a='+OR+'1'='1                                     │
│ user_agent    │ curl/7.81.0                                         │
╰───────────────┴─────────────────────────────────────────────────────╯

 - Events  :

- Date: 2026-03-12 09:49:03 +0000 UTC
╭───────────────┬──────────────────────────╮
│      Key      │           Value          │
├───────────────┼──────────────────────────┤
│ data          │                          │
│ matched_zones │ REQBODY_PROCESSOR        │
│ message       │ Enabling body inspection │
│ rule_name     │ native_rule:901340       │
│ target_fqdn   │ localhost                │
│ uri           │ /?a='+OR+'1'='1          │
╰───────────────┴──────────────────────────╯

- Date: 2026-03-12 09:49:03 +0000 UTC
╭───────────────┬──────────────────────────────────────────────────────╮
│      Key      │                         Value                        │
├───────────────┼──────────────────────────────────────────────────────┤
│ data          │ Matched Data: s&sos found within ARGS:a: ' OR '1'='1 │
│ matched_zones │ ARGS.a,ARGS.a                                        │
│ message       │ SQL Injection Attack Detected via libinjection       │
│ rule_name     │ native_rule:942100                                   │
│ target_fqdn   │ localhost                                            │
│ uri           │ /?a='+OR+'1'='1                                      │
╰───────────────┴──────────────────────────────────────────────────────╯

- Date: 2026-03-12 09:49:03 +0000 UTC
╭───────────────┬──────────────────────────────────────────────────╮
│      Key      │                       Value                      │
├───────────────┼──────────────────────────────────────────────────┤
│ data          │                                                  │
│ matched_zones │ TX.blocking_inbound_anomaly_score                │
│ message       │ Inbound Anomaly Score Exceeded (Total Score: 10) │
│ rule_name     │ native_rule:949110                               │
│ target_fqdn   │ localhost                                        │
│ uri           │ /?a='+OR+'1'='1                                  │
╰───────────────┴──────────────────────────────────────────────────╯

- Date: 2026-03-12 09:49:03 +0000 UTC
╭───────────────┬──────────────────────────────────────────────────────────────╮
│      Key      │                             Value                            │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ data          │                                                              │
│ matched_zones │ UNKNOWN                                                      │
│ message       │ Anomaly Scores: (Inbound Scores: blocking=10, detection=10,  │
│               │ per_pl=10-0-0-0, threshold=5) - (Outbound Scores:            │
│               │ blocking=0, detection=0, per_pl=0-0-0-0, threshold=4) -      │
│               │ (SQLI=10, XSS=0, RFI=0, LFI=0, RCE=0, PHPI=0, HTTP=0,        │
│               │ SESS=0, COMBINED_SCORE=10)                                   │
│ rule_name     │ native_rule:980170                                           │
│ target_fqdn   │ localhost                                                    │
│ uri           │ /?a='+OR+'1'='1                                              │
╰───────────────┴──────────────────────────────────────────────────────────────╯

It’s almost the same, except for the reason: anomaly score block: sql_injection: 10, anomaly: 10,.

The presence of block means the request was actually blocked and never reached the target application.

Customizing CRS for Your Applications

Out of the box, CRS is tuned for broad coverage. For any moderately complex application, you’ll likely need some customization to eliminate false positives. CrowdSec uses the CRS plugin system for all customizations, keeping your changes separate from the CRS rules themselves.

CRS Plugins for Popular Applications

The CRS community maintains exclusion plugins for popular web applications. These plugins contain pre-built rule exclusions that prevent common false positives for each application.

The following plugins are available directly from the CrowdSec Hub:

  • WordPress
  • NextCloud
  • PHPMyAdmin
  • CPanel
  • Drupal
  • PHPBB
  • DokuWiki
  • Xenforo

Installing a plugin

cscli collections install crowdsecurity/appsec-crs-exclusion-plugin-

For example, to install the WordPress plugin:

cscli collections install crowdsecurity/appsec-crs-exclusion-plugin-wordpress

Once installed, the plugin loads automatically after a CrowdSec restart.

The plugins are updated automatically, like any other Hub items, so you will receive upstream changes automatically.

You can also manually deploy custom plugins; refer to our documentation.

Putting It All Together: Defense in Depth

Here’s how a complete AppSec configuration might look, combining virtual patching and CRS:

# /etc/crowdsec/acquis.d/waf.yaml
source: appsec
appsec-configs:
  - crowdsecurity/appsec-default # Virtual patching (in-band)
  - crowdsecurity/crs # OWASP CRS (out-of-band)
labels:
  type: appsec

With this setup:

  1. Virtual patching rules run in-band, immediately blocking requests that match known CVE patterns
  2. CRS rules run out-of-band, detecting broad attack patterns and feeding events into CrowdSec’s scenario engine
  3. IPs that trigger multiple CRS violations get banned automatically through the scenario system
  4. CrowdSec’s threat intelligence adds another layer, sharing decisions across the community

Once you’ve tuned CRS and are confident in your configuration, you can switch to the in-band CRS collection for immediate blocking with the following configuration:

# /etc/crowdsec/acquis.d/waf.yaml
source: appsec
appsec-configs:
  - crowdsecurity/appsec-default # Virtual patching (in-band)
  - crowdsecurity/crs-inband # OWASP CRS (in-band)
labels:
  type: appsec

Next Steps

Now that you have deployed CRS with CrowdSec, here are some more resources and tools you can use to better protect your web applications:  

WRITTEN BY

You may also like

Strengthen Security and Protection with CrowdSec’s Open Source Web Application Firewall
Tutorial

Strengthen Security and Protection with CrowdSec’s Open Source Web Application Firewall

Block 75% of malicious traffic before it ever reaches your server with CrowdSec’s open source web application firewall.

caddy and crowdsec
Tutorial

Secure Caddy with CrowdSec: Remediation and WAF Guide

Learn how to secure Caddy with CrowdSec using the Remediation and AppSec components. Step-by-step setup for blocking threats and logging traffic.

Securing Automated Application Deployment with CrowdSec and Coolify
Tutorial

Securing Automated Application Deployment with CrowdSec and Coolify

Learn how to secure your automated app deployments with CrowdSec and Coolify and defend against abusive IPs and unwanted traffic.