Skip to content

OWASP Core Rule Set (CRS)

The OWASP Core Rule Set is the most widely used, battle-tested set of security rules for web applications. kubeWAF has first-class support for it.

What You Get

When you set crsEnable: true on a WAF, the operator:

  1. Includes all pre-converted CRS SecRule objects (or merges them at runtime)
  2. Automatically loads the recommended initialization rules (paranoia levels, anomaly thresholds, etc.)
  3. Respects the same phase and chaining semantics as the original CRS

You still get the full power of CRS plus any of your own custom rules.

Enabling CRS

The simplest way:

apiVersion: waf.kubewaf.io/v1beta1
kind: WAF
metadata:
  name: protect-all
spec:
  crsEnable: true
  ruleRefs:
  - kind: RuleSet
    name: my-custom-rules
  parentRefs:
    targetRef:
      kind: HTTPRoute
      name: my-app

Your custom rules and the CRS rules run together. The CRS anomaly scoring rules will see scores contributed by both.

CRS Version & Samples

kubeWAF ships with converted rules for CRS v4.3.0 (and newer versions can be converted).

The converted rules live in config/samples/crs/ and are labeled:

metadata:
  labels:
    app.kubernetes.io/part-of: coreruleset
    coreruleset/version: 4.3.0
    coreruleset/file: REQUEST-920-PROTOCOL-ENFORCEMENT.conf

A convenient RuleSet that selects the entire CRS is also provided:

# config/samples/crs/crs-ruleset.yaml
kind: RuleSet
metadata:
  name: crs
spec:
  ruleRefs:
  - kind: SecRule
    namespace: default     # change as needed
    selector:
      matchLabels:
        coreruleset/version: 4.3.0

You can reference this RuleSet or create your own selector-based one.

Paranoia Levels & Thresholds

CRS v4 uses paranoia levels (1–4). Higher levels = more aggressive detection but higher chance of false positives.

The initialization rules (usually REQUEST-901-INITIALIZATION) set:

  • tx.detection_paranoia_level
  • tx.inbound_anomaly_score_threshold
  • tx.outbound_anomaly_score_threshold

You can override these values with your own SecRule that runs early in phase 1 and sets the TX variables.

Example (recommended for most applications):

- metadata: { id: 900100, phase: "1" }
  conditions: [{ always-match: true }]
  actions:
    nonDisruptive:
    - nonDisruptiveActionType: setvar
      value: tx.detection_paranoia_level=2
    - nonDisruptiveActionType: setvar
      value: tx.inbound_anomaly_score_threshold=7
    - nonDisruptiveActionType: setvar
      value: tx.outbound_anomaly_score_threshold=5

Using the CRS Converter Tool

If you want to upgrade to a newer CRS release or customize the conversion:

# Build the tool
make crs-converter

# Convert a full CRS checkout
bin/crs-converter \
  --input=/path/to/coreruleset/rules \
  --output-dir=config/samples/crs-new \
  --crs-version=4.4.0 \
  --namespace=platform-security

The tool:

  • Parses the original .conf files using crslang
  • Emits one SecRule per original file (containing all rules from that file)
  • Adds rich labels (coreruleset/file, coreruleset/version)
  • Preserves chaining, markers, phases, and transformations

Mixing CRS with Custom Rules

Order of evaluation is determined by rule id and phase, exactly like native ModSecurity.

A typical effective combination:

  1. Early initialization rules (your overrides)
  2. CRS rules (via crsEnable or explicit RuleSet)
  3. Your own "allow-list" or "exception" rules (using SecAction + ctl:ruleRemoveById)
  4. Final blocking / anomaly scoring rules

Performance Considerations

CRS contains ~300–400 rules. Most of them are cheap regex or byte checks. On modern hardware the overhead per request is usually < 1–2 ms.

If you are extremely latency-sensitive:

  • Start with paranoia level 1 + higher anomaly threshold
  • Exclude rules you know are irrelevant (e.g., PHP-specific rules in a Java shop)
  • Use SecAction + ctl:ruleRemoveById or ctl:ruleRemoveByTag

Troubleshooting CRS

Too many false positives?

  • Lower the paranoia level
  • Raise the anomaly score threshold
  • Add exclusion rules early

Nothing is being blocked?

  • Make sure crsEnable: true (or the CRS RuleSet is referenced)
  • Check the WAF logs (Envoy Gateway logs + WASM filter logs)
  • Verify the ReferencesResolved condition on your WAF

I want only part of CRS

Create a custom RuleSet that selects only the files you care about:

selector:
  matchExpressions:
  - key: coreruleset/file
    operator: In
    values: ["REQUEST-920-PROTOCOL-ENFORCEMENT.conf", "REQUEST-942-APPLICATION-ATTACK-SQLI.conf"]

Further Reading

Next: learn exactly how to attach these rules to Envoy Gateway routes.