Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions content/3-reference-manual/3-2-configuration-directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -1228,11 +1228,39 @@ SecRule ARGS "@rx attack" \

The addition of t:none will neutralize any previous transformation functions specified (t:lowercase, in the example).

{{% notice note %}}
### Updating chained rules

In ModSecurity v2, if the target rule is a chained rule, you must currently specify chain in the SecRuleUpdateActionById action list as well. In libModSecurity v3, action updates to a chained rule may only be made to the main (first) rule in the chain, and the `:offset` suffix is not supported (the ID is parsed as a number).
The two engines handle updates to rules that are part of a [chain]({{% relref "3-reference-manual/3-5-actions#chain" %}}) differently.

{{% /notice %}}
**ModSecurity v2** merges the new actions into the rule itself, and the merge always takes the chain flag from the new action list:

- If the target rule has the chain action, **repeat chain in the update**. Otherwise the update removes the rule from the chain: the rules that followed it are then evaluated on their own, and their actions run even when the original chain would not have matched.
- Do not add chain when updating a rule that does not have it, such as the last rule of a chain. It has been reported that the msg of a chain updated this way is no longer logged.
- `RULEID:offset` targets a rule inside a chain: offset 0 (the default) is the chain starter, 1 the rule after it, and so on.

``` seclang
SecRule REQUEST_HEADERS:User-Agent "@rx ^curl" \
"id:12346,\
phase:2,\
pass,\
log,\
msg:'Example chain',\
chain"
SecRule REQUEST_HEADERS:Accept "@rx \*/\*" \
"t:none"

# Wrong in v2: unchains rule 12346, so the Accept rule runs on its own
SecRuleUpdateActionById 12346 "deny,status:403"

# Right in v2: the chain is kept
SecRuleUpdateActionById 12346 "deny,status:403,chain"
```

**libModSecurity v3** keeps the rule unchanged and applies the updated actions at run time, whenever the rule with that ID is evaluated:

- Only the chain starter can be updated, since the other rules of a chain have no ID. The `:offset` suffix is not supported (the ID is parsed as a number).
- The chain is never broken, so there is no need to repeat chain. If chain is repeated anyway, for configurations shared with v2, it is ignored and a "General failure" message is printed when the rules are loaded.
- Every action added by the update, including setvar, runs only when the whole chain matches. This is different from a setvar written on the chain starter itself, which runs as soon as the chain starter matches.

## SecRuleUpdateTargetById

Expand Down
33 changes: 32 additions & 1 deletion content/3-reference-manual/3-5-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,40 @@ The following directives can be used in rule chains:
Special rules control the usage of actions in chained rules:

- Any actions that affect the rule flow (i.e., the disruptive actions, skip and skipAfter) can be used only in the chain starter. They will be executed only if the entire chain matches.
- Non-disruptive actions can be used in any rule; they will be executed if the rule that contains them matches and not only when the entire chain matches. In libModSecurity v3 this is true only for setvar: every other non-disruptive action in a chained rule (ctl, setenv, exec, log, ...) runs only once the whole chain has matched.
- Non-disruptive actions can be used in any rule of the chain. The rules of a chain are evaluated in order, and evaluation stops at the first rule that does not match. A non-disruptive action therefore runs once the rule that contains it **and every rule before it** in the chain have matched; the rules after it play no part. The engines differ in which actions this applies to:
- **ModSecurity v2**: every non-disruptive action (setvar, ctl, setenv, exec, ...).
- **libModSecurity v3**: only setvar. Every other non-disruptive action in a chained rule (ctl, setenv, exec, ...) runs only once the whole chain has matched.
- The metadata actions (e.g., id, rev, msg) can be used only in the chain starter.

Example, with a request whose User-Agent starts with `curl`, whose Accept header is `*/*` and whose Host is `localhost`:

``` seclang
SecAction \
"id:106,\
phase:1,\
pass,\
nolog,\
setvar:'tx.counter=0'"

SecRule REQUEST_HEADERS:User-Agent "@rx ^curl" \
"id:107,\
phase:2,\
pass,\
log,\
msg:'Example chain',\
setvar:'tx.counter=+1',\
chain"
SecRule REQUEST_HEADERS:Accept "@rx \*/\*" \
"setvar:'tx.counter=+2',\
chain"
SecRule REQUEST_HEADERS:Host "@rx ^example\.com$" \
"setvar:'tx.counter=+4'"
```

The first two rules match and the third does not, so the chain as a whole does not match, but `tx.counter` ends up as 3: the setvar actions of the first two rules ran. If the User-Agent did not start with `curl`, `tx.counter` would stay 0 even though the Accept rule would have matched, because the rest of the chain is never evaluated once a rule fails.

Take extra care when changing the actions of a chained rule with SecRuleUpdateActionById: the two engines treat chains differently there, see [SecRuleUpdateActionById]({{% relref "3-reference-manual/3-2-configuration-directives#secruleupdateactionbyid" %}}).

## ctl

Changes ModSecurity configuration on transient, per-transaction basis. Any changes made using this action will affect only the transaction in which the action is executed. The default configuration, as well as the other transactions running in parallel, will be unaffected.
Expand Down
Loading