skip to content

What does ZAP's scanner.excludeAntiCsrfTokens setting exclude from an active scan, and what does it miss?

level: middleimportance: nice to knowfreq 26%

answer

  1. two settings, similar names, opposite jobs
  2. on by default, and narrow
  3. the type test is the limitation
  4. post data only, by name recognition

basics

~20 s

It is on by default and skips a parameter when that parameter is post data and core's anti-CSRF extension recognises its name as a token. A token in the query string or a JSON body is still attacked.

solid answer

~40 s

The check lives in `AbstractAppParamPlugin.isToExclude`, and it has two conditions joined by `and`: the parameter's type must be post data, **and** the anti-CSRF extension must recognise the name as a token. Only then is the parameter skipped. So a token carried as a query parameter, inside a JSON body, or in a custom header is not covered by this setting at all. Its purpose is to stop rules wasting requests on a value the application regenerates every response, and to avoid breaking the request that carries it. Keep it apart from the similarly named `scanner.antiCSRF`, which is a different setting doing the opposite work: that one makes the engine fetch a fresh token so attack requests stay valid. Both default to true.

code

bash · 2 lines
bash
zap.sh -cmd -autorun /zap/wrk/plan.yaml \
  -config scanner.excludeAntiCsrfTokens=false

go deeper

for a junior

Know that recognised anti-CSRF tokens in a form body are skipped by default, and that a second, similarly named setting keeps those tokens fresh.

for a middle

State both conditions of the check and name what falls outside it, particularly a token carried as a JSON field or a query parameter.

for a senior

Reach for the excluded-parameter list when your own rotating values are being attacked, and explain why disabling the exclusion is the wrong lever.

for a principal

Decide where such exclusions are recorded. A value excluded from attack is a documented coverage gap, and it belongs in the same artefact as the vector set.

## Two settings with almost the same name The first thing to get right is which setting you are talking about, because both live under the `scanner` config prefix, both default to true, and they do opposite things. | config key | field on `ScannerParam` | what it does | |---|---|---| | `scanner.excludeAntiCsrfTokens` | `excludeAntiCsrfTokens` | skip recognised token parameters instead of attacking them | | `scanner.antiCSRF` | `handleAntiCSRFTokens` | refresh the token before sending, so the attack request is accepted | Read them together and the design is clear: **do not attack the token, but do keep it valid** so that the parameter next to it can be attacked without the application rejecting the whole request. ## Where the exclusion is actually applied It is not applied by the factory that builds input vectors, and it is not a vector of its own. It is applied one level down, in the per-parameter loop of the rule base class. For each pair a variant offers, `isToExclude` runs, and the anti-CSRF branch fires only when **both** of these hold: - the pair's type is post data — the type a form-body variant assigns; - core's anti-CSRF extension recognises the pair's name as a token it is tracking. Miss either condition and the parameter is attacked like any other. ## What that leaves uncovered The type test is the interesting half, because tokens do not only travel in form bodies. - **A token in the query string** carries the query type, not post data, so it is attacked. - **A token inside a JSON body** is offered by the JSON variant with its own type, so it is attacked. - **A token in a custom header** is only ever offered when the header vector is on — and the header variant separately refuses to touch the well-known CSRF-token header names, which is a different mechanism with a similar effect. None of this is a bug. It is a narrow, type-specific rule, and knowing its shape tells you why a scan against an API that sends its token as a JSON field spends requests on a value that will never be vulnerable and will usually be rejected. There is a reason the test is that narrow. The body-parameter type is assigned by the form-body variant, which is the shape the exclusion was written for: a hidden form field carrying a per-response token. Every other encoding gets its own type from its own variant — JSON fields, multipart parts, query parameters — and the exclusion was never extended to them. So the rule of thumb is not "tokens are excluded" but "**recognised tokens in a form body are excluded**". ## The neighbouring, more general mechanism Beside the anti-CSRF branch, the same `isToExclude` consults the **excluded-parameter filter list** — a general list of parameter names, optionally constrained to a type and a URL pattern. That is the mechanism to reach for when a session identifier, a signature, or a cache-busting value is being attacked pointlessly, because it is not limited to post data and it does not depend on anything recognising the value as a token. ## Where you can and cannot set it 1. **A startup config key** — `-config scanner.excludeAntiCsrfTokens=false`. This is the headless route. 2. **The control API** — `ScannerParam` is registered as `ascan` options, so every setter becomes an option action, this one included. 3. **The desktop options panel**, as a checkbox beside the other active-scan settings. 4. **Not from an automation plan.** Neither the `activeScan` job nor the `activeScan-config` job declares a parameter with this name, and both apply their YAML onto their own typed parameter objects, so an unrecognised key is reported rather than applied. This is the same shape as the plain-body vector: shipped behaviour that the plan schema simply does not reach. ## What to say about it in a review If someone proposes turning the exclusion off to "scan more thoroughly", the honest answer is that it buys requests against a value the application rotates, and it risks invalidating every request in the group. The better move is almost always the opposite direction: leave it on, and use the excluded-parameter list to add the values your own application rotates but no extension recognises.

  • Why exclude a token from attack rather than simply letting rules try it?
    Two reasons. The value is regenerated by the application, so payloads in it mean nothing, and a modified token usually makes the target reject the entire request — which wastes the run and can make every other parameter in that request look clean.
  • What would you use for a value the token recognition does not know about?
    The excluded-parameter filter list on the active-scan configuration. Each entry is a parameter name with an optional type and URL pattern, so it reaches query parameters and body parameters alike, and it does not depend on anything classifying the value as a token.

saying these in an interview costs you the question

  • Confuses the exclusion setting with the token-handling setting
  • Thinks the exclusion covers tokens anywhere in the request
  • Says the setting is off by default so tokens are attacked
  • Expects to set it from an automation plan job
  • Recommends disabling it to increase scan coverage