Skip to content

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

OWASP Core Rule Set

Page as Markdown

Enable the bundled OWASP Core Rule Set (CRS) to protect against a broad range of common web attacks.

About the OWASP Core Rule Set

Web applications are constantly exposed to a broad range of attacks, including SQL injection, cross-site scripting (XSS), remote code execution (RCE), local file inclusion, and more. These attacks are well-understood and cataloged by the OWASP Top Ten. Building and maintaining rules that can detect each attack is a significant effort. Without a managed rule set, teams must write and update individual rules for every known attack vector, and keep up with new threats as they emerge.

The OWASP Core Rule Set (CRS) is an open source set of generic detection rules that protect against a wide range of attacks, including the OWASP Top Ten. Solo Enterprise for kgateway bundles CRS version 4.

CRS rules are assigned a unique ID and are organized into the same four Coraza processing phases that are described in the WAF server architecture section. Each phase targets a different part of the HTTP transaction.

The following table shows which groups of CRS rules run in each phase.

Each CRS rule is assigned a specific ID.

PhaseNameWhen it runsExample CRS rules
1Request headersAfter request headers are received, before the body913100 (scanner User-Agent detection), 920430 (HTTP protocol version), 930130 (restricted file paths)
2Request bodyAfter the full request body is received942xxx (SQL injection), 941xxx (XSS), 932xxx (RCE)
3Response headersAfter response headers are received from the upstream950xxx (data leakage detection)
4Response bodyAfter the full response body is received951xxx–956xxx (data leakage in response body)

To use the CRS, add the coreRuleSet.settings field to your WAFPolicy. The following rules apply:

  • You must enable the Coraza rule engine by setting SecRuleEngine On in ruleEngineSettings.
  • At a minimum, you must set the crs_setup_version transaction variable in phase:1 by using an ID in the 900xxx range. The CRS rule 901001 checks that this variable is set. If the rule is missing, CRS blocks all requests with a critical error.
  • You can specify which CRS rules you want to apply in each phase by using the SecDefaultAction directive in the coreRuleSet.settings field.
  • You can use customDirectives to exclude or modify individual CRS rules.
  • You can use CRS rules together with custom rules to layer app-specific rules on top of generic CRS protections.

Before you begin

  1. Follow the Get started guide to install Solo Enterprise for kgateway.

  2. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.

  3. Get the external address of the gateway and save it in an environment variable.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS  
    kubectl port-forward deployment/http -n kgateway-system 8080:8080

  1. Enable the WAF server for your GatewayClass.

Configure the OWASP Core Rule Set

In this example, you enable CRS Phase 1 (request header) and Phase 2 (request body) rules for the httpbin app. If a request matches any of these rules, the request is denied with a 403 HTTP response. You also use the customDirectives setting to exclude two CRS rules.

  1. Create the WAFPolicy with your CRS rules.

    kubectl apply -f- <<EOF
    apiVersion: waf.solo.io/v1alpha1
    kind: WAFPolicy
    metadata:
      name: httpbin-waf
      namespace: httpbin
    spec:
      coreRuleSet:
        settings:
          inline: |
            SecDefaultAction "phase:1,log,auditlog,deny,status:403"
            SecDefaultAction "phase:2,log,auditlog,deny,status:403"
            SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
      ruleEngineSettings:
        inline: |
          SecRuleEngine On
      customDirectives:
        - inline: |
            SecRuleRemoveById 913100
            SecRuleRemoveById 920430
    EOF
    SettingDescription
    coreRuleSet.settingsConfigures the OWASP CRS. SecDefaultAction "phase:1,..." and SecDefaultAction "phase:2,..." set the default action for all Phase 1 (request header) and Phase 2 (request body) CRS rules to log and deny with a 403 status. SecAction id:900990 is the required CRS initialization action that sets the tx.crs_setup_version variable. Without this variable, CRS rule 901001 blocks all requests. For more information, see About the OWASP Core Rule Set.
    ruleEngineSettingsRequired. Enables the Coraza rule engine with SecRuleEngine On.
    customDirectivesRemoves two Phase 1 rules that cause false positives in this environment. Rule 913100 (REQUEST-913 Scanner Detection) blocks User-Agents that are associated with security scanners such as sqlmap. Rule 920430 (REQUEST-920 Protocol Enforcement) validates the HTTP protocol version. You remove this rule, because the ExtProc integration in Solo Enterprise for kgateway does not forward the original HTTP protocol version, which can cause all requests to get denied.
  2. Create the EnterpriseKgatewayTrafficPolicy to apply the policy.

    kubectl apply -f- <<EOF
    apiVersion: enterprisekgateway.solo.io/v1alpha1
    kind: EnterpriseKgatewayTrafficPolicy
    metadata:
      name: httpbin-waf
      namespace: httpbin
    spec:
      targetRefs:
        - group: gateway.networking.k8s.io
          kind: HTTPRoute
          name: httpbin
      entWAF:
        wafPolicyRef:
          name: httpbin-waf
    EOF
  3. Send a request to the httpbin app. Verify that you get a 200 response.

    curl -i http://$INGRESS_GW_ADDRESS:8080/status/200 -H "host: www.example.com:8080"
    curl -i localhost:8080/status/200 -H "host: www.example.com"

    Example output:

    HTTP/1.1 200 OK
    ...
  4. Send a request that triggers a Phase 1 CRS rule. The .htaccess path matches rule 930130 from the REQUEST-930 Local File Inclusion (LFI) rule group, which blocks requests that attempt to access restricted server configuration files. Verify that the WAF server blocks the request with a 403 response.

    curl -vik "http://$INGRESS_GW_ADDRESS:8080/.htaccess" \
      -H "host: www.example.com:8080"
    curl -vik "localhost:8080/.htaccess" \
      -H "host: www.example.com"

    Example output:

    HTTP/1.1 403 Forbidden
    ...

Next

Cleanup

You can optionally remove the resources that you set up as part of this guide.
kubectl delete wafpolicy -n httpbin httpbin-waf
kubectl delete enterprisekgatewaytrafficpolicy -n httpbin httpbin-waf

Other configurations

Review other common configurations.

Detection-only mode

Evaluate CRS rules against production traffic without blocking requests by setting the rules engine to DetectionOnly. You also change the default actions to pass so that violations are logged, but traffic is not denied. This setup lets you identify false positives before switching to enforcement.

apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
  name: crs-detection-only
  namespace: httpbin
spec:
  coreRuleSet:
    settings:
      inline: |
        SecDefaultAction "phase:1,log,auditlog,pass"
        SecDefaultAction "phase:2,log,auditlog,pass"
        SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
  ruleEngineSettings:
    inline: |
      SecRuleEngine DetectionOnly

CRS with IP filtering

Layer CRS on top of IP filtering. This way, traffic is inspected for the OWASP Top Ten attacks and allowed through only if sent from specific IP addresses. You define IP filtering rules in the customDirectives.

apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
  name: crs-with-ip-allowlist
  namespace: httpbin
spec:
  coreRuleSet:
    settings:
      inline: |
        SecDefaultAction "phase:1,log,auditlog,deny,status:403"
        SecDefaultAction "phase:2,log,auditlog,deny,status:403"
        SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
  ruleEngineSettings:
    inline: |
      SecRuleEngine On
  customDirectives:
    - inline: |
        SecRule REMOTE_ADDR "!@ipMatch 203.0.113.0/24,198.51.100.50" \
          "phase:1,deny,status:403,id:1,msg:'IP not in allowlist'"
    - inline: |
        SecRuleRemoveById 913100
        SecRuleRemoveById 920430

CRS with custom rules

Add application-specific rules alongside CRS. CRS covers generic attack patterns, but you can add custom directives for business logic that CRS does not address, such as blocking a malicious User-Agent.

apiVersion: waf.solo.io/v1alpha1
kind: WAFPolicy
metadata:
  name: crs-with-custom
  namespace: httpbin
spec:
  coreRuleSet:
    settings:
      inline: |
        SecDefaultAction "phase:1,log,auditlog,deny,status:403"
        SecDefaultAction "phase:2,log,auditlog,deny,status:403"
        SecAction "id:900990,phase:1,pass,t:none,nolog,tag:'OWASP_CRS',ver:'OWASP_CRS/4.23.0',setvar:tx.crs_setup_version=4230"
  ruleEngineSettings:
    inline: |
      SecRuleEngine On
  customDirectives:
    - inline: |
        SecRule REQUEST_HEADERS:User-Agent "@streq bad-bot" \
          "deny,status:403,id:5001,phase:1,msg:'blocked bad-bot User-Agent'"
    - inline: |
        SecRuleRemoveById 913100
        SecRuleRemoveById 920430
Was this page helpful?