The new netnea-CRS-Upgrading-Plugin: Simplifying the Migration from CRS v3 to v4   Recently updated !


Migrating from CRS v3 to CRS v4 can be intimidating. It’s a complicated task that risks to leave you vulnerable during the transition. But with the help of the new netnea-CRS-Upgrading-Plugin you can keep your guards up during the transition.

Introduction

Upgrading the OWASP CRS ruleset from version 3 to version 4 is not as trivial as simply increasing a version number. This is reflected by the fact that, even though CRS v4 was released in February 2024, a large number of CRS v3 installations are still in active use.

Between CRS v3 and v4, many rules have changed significantly, partly due to insights gained from our private bug bounty program in 2023. New rules were added and existing rules were tightened or expanded based on real-world vulnerability reports. While this makes CRS v4 the most secure CRS release line we’ve ever released, it also means that upgrading involves more work than simply switching versions. The risk of new false positives (FPs) is high, requiring a careful tuning phase.

Tuning a fresh CRS installation typically follows an iterative approach: install CRS with a high threshold -> tune FPs -> lower the threshold -> tune again -> repeat until reaching the desired threshold. However, if you already have a well-tuned CRS v3 deployment running at a low threshold, following this approach would require temporarily increasing the threshold again. You would then install CRS v4 and repeat the iterative tuning process until the threshold can be lowered back. In production, this temporary elevation of the threshold significantly reduces security, which is not acceptable.

To address this problem, I developed a CRS plugin that allows you to introduce CRS v4 alongside your production CRS v3 deployment, without raising thresholds and without reducing security during the transition.

This blog post is the first in a three-part series. Here I’ll give you a broad overview and in the latter installments, I will then cover the implementation of the plugin and then the practical migration step by step.

Prerequisites: Parallel installation of CRS v4 alongside CRS v3

The first step is to install CRS v4 in parallel to the existing CRS v3 installation. The simplest approach is to place the CRS v4 folder next to the existing CRS directory. Because both versions contain rules with overlapping IDs, we must renumber the CRS v4 rule IDs from the 9xxxxx range to a different range. I use the 89xxxxx range. This separate rule block also makes log filtering easier. Next, we remove the blocking rules 894911089491118959100 and 8959101 in CRS v4 so that CRS v4 initially runs in log-only mode.

The inbound anomaly score variable name changed between v3 and v4:

  • CRS v3: tx.anomaly_score_pl1
  • CRS v4: tx.inbound_anomaly_score_pl1

This prevents interference between the two versions’ inbound scoring. Unfortunately, the outbound variable name did not change: it remains tx.outbound_anomaly_score_pl1. To avoid score collisions, we must adjust the outbound variable name in CRS v4 to: tx.outbound_anomaly_score_pl1_crs4.

Checking the old crs-setup.conf and aligning it with the new one

After installing CRS v4, install the new crs-setup.conf. Several important changes were introduced between the v3 and v4 versions that you must address:

If you used application-specific exclusions via rule 900130, note that this rule no longer exists in v4. Application-specific exclusions are now handled by CRS plugins. See the official documentation for details: https://coreruleset.org/docs/4-about-plugins/4-1-plugins/. For example, the following CRS v3 rule needs to be migrated or replaced with a plugin configuration:

SecAction \
 "id:900130,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.crs_exclusions_cpanel=1,\
  setvar:tx.crs_exclusions_drupal=1,\
  setvar:tx.crs_exclusions_dokuwiki=1,\
  setvar:tx.crs_exclusions_nextcloud=1,\
  setvar:tx.crs_exclusions_wordpress=1,\
  setvar:tx.crs_exclusions_xenforo=1"

DoS protection has also moved into a separate CRS v4 plugin: https://github.com/coreruleset/dos-protection-plugin-modsecurity

If you used rule 900280, be aware that the format for allowed charset values has changed. In CRS v3 you might have used:

  • tx.allowed_request_content_type_charset=utf-8|iso-8859-1|iso-8859
    In CRS v4, the values must be prefixed with a leading bar:
  • tx.allowed_request_content_type_charset=|utf-8| |iso-8859-1| ....

Step 1: Install the netnea-crs-upgrading-plugin: CRS v4 in log-only mode

Once CRS v4 is installed and the configuration is aligned, you can install the netnea-crs-upgrading-plugin. Installation follows the standard plugin process.

By default, the plugin operates in parallel mode. In this mode, CRS v4 runs first in log-only mode, and then CRS v3 runs in full blocking mode. During this phase, the majority of tuning can be performed safely. Your logs will contain alerts produced by the new CRS v4 ruleset, giving you the chance to identify and eliminate false positives without affecting production traffic.

This parallel operation is also a rare opportunity to reevaluate your existing exclusions. If your v3 installation contains overly broad or outdated exclusions, parallel mode lets you observe real traffic again and verify whether narrower or updated exclusions would work better.

This is also the stage where you must decide whether to migrate existing v3 tunings into the v4 configuration or start fresh. Sometime so much effort has been put into the tuning of rules, it makes sense to review the existing exclusions and transform them to rule exclusions for CRS v4. However, this inevitably carries legacy complexity into your new environment.
Starting from zero is the cleanest option and if you choose that route, our tool C-Rex Arms can help you generate proper rule exclusions.

C-Rex

C-Rex is a suite of tools provided by netnea. C-Rex supports the handling as well as the identification of false positives (in large amounts of traffic). Aimed at enterprise setups, it reduces the time needed for log analysis and it allows developers to handle the WAFs themselves.
More about C-Rex: https://c-rex.netnea.com.

Step 2: CRS v4 begins blocking

After running CRS v4 in log-only mode long enough to feel confident, you can begin enabling blocking selectively. This brings us to step 2.

Step 2a: Path-based rollout

The recommended approach is a path-based rollout. Some parts of your application may already be well-tuned and fully compatible with CRS v4, while others may require more tuning or are considered legacy and not worth adjusting.
You can configure specific paths or endpoints to use CRS v4 in blocking mode, while the rest continue to be protected by CRS v3. This allows a controlled, low-risk transition.
During this phase, you can gradually expand the set of paths handled by CRS v4. Over time, more and more paths will be migrated to CRS v4, increasing the portion of production traffic that is processed by the new ruleset.

Step 2b: Sampling mode

For the remaining, unassigned paths, the plugin provides a sampling mode. Here you can specify the percentage of requests that should pass through CRS v4, while the remaining requests continue to be evaluated by CRS v3. This mechanism offers a smooth, controlled way to expose real production traffic to CRS v4 without immediately committing the entire application to the new ruleset. By starting with a low sampling percentage, you can observe the behavior of CRS v4 under realistic load while keeping the risk low.
As confidence increases and false positives become less frequent, you can gradually raise the sampling percentage in small increments. This step-by-step approach ensures that any unexpected issues remain contained. Eventually, once you reach 100% sampling, all traffic is handled by CRS v4 in blocking mode, and CRS v3 no longer processes them. This marks the final phase before completely removing CRS v3 from the environment.

End of the upgrading process

The upgrade is complete once all paths have been migrated to CRS v4 or once sampling reaches 100%. At this point CRS v3 with its exclusion rules can be removed entirely.
You may then:

  • renumber the CRS v4 rule IDs and exclusion rules from 89xxxxx back to the standard 9xxxxx range,
  • and revert the temporary variable name tx.outbound_anomaly_score_pl1_crs4 to its original form.

This blog post is part of a three-part series. In the next post, I’ll cover the technical implementation details of the netnea-crs-upgrading-plugin.

The netnea-crs-upgrading plugin can be found at: https://github.com/netnea/netnea-crs-upgrading-plugin

Fränzi at the top of the Milan Cathedral during the CRS developer retreat in 2022

Franziska Bühler