Anti-CSRF tokens change on each form load, so a scanner that replays an old request gets rejected and never reaches the code behind the form. The fix is a scanner that fetches a fresh token before each attack request, plus a check that those requests really succeed.
What the token does
A cross-site request forgery attack tricks a logged-in user's browser into sending a request they did not intend. An anti-CSRF token defends against it. The server puts a random value in each form, and only accepts a submission that carries the matching value. An attacker's page cannot read it, so it cannot forge a valid request.
Why scanners struggle
The ZAP documentation puts the clash plainly: these tokens protect against CSRF, and they also make a tester's job harder, especially when they are regenerated every time a form is requested. A scanner that submits a payload with a stale token gets an error page. It may log "no vulnerability" for a form it never tested. Your report then looks clean for the wrong reason.
How scanners cope
ZAP's documentation says it detects anti-CSRF tokens by attribute names, using a configurable list of names. When it recognizes one, it requests a fresh value before each submission. Its options also describe a setting to treat form parameter names as tokens if they include one of the listed names. Other tools use similar approaches, such as recorded login-and-form sequences or custom scripts.
The ZAP active scan options add a caution: if you use more than one thread, check that tokens are being generated correctly, for example by using custom tags to check for success or failure patterns in the response.
Steps to check your own app
- Find the token name. Open the form source and look for a hidden field or a header.
- Add the name to the scanner if it is custom, such as a project-specific field.
- Run one form by hand with the scanner's proxy and confirm the server accepts the submission.
- Read the scan log. Look for a high rate of 403 or 400 responses on POST requests.
- Add a success check. Many scanners let you define text that proves a form worked, such as a confirmation message.
- Reduce threads if tokens still fail. A single thread is slower but safer for single-use tokens.
- Ask for a test switch. In a test environment, some teams add a setting that relaxes token checks for the scanner account only. Keep it off in production and document it.
Common failure signs
| Sign in the log | Likely cause |
|---|---|
| Many 403 responses on POST | Stale or missing token |
| Same error text on every payload | Form rejected before reaching logic |
| Fewer findings after you enable CSRF protection | Scanner no longer reaching the handler |
| Works with one thread, fails with many | Single-use token raced between threads |
Do not remove the protection to make scans easier
It is tempting to turn token checks off in the test environment so scans pass. That hides the very control you want to keep working. Better to teach the scanner about the token, or relax it only for a named test account in a non-production environment.
A worked example
A team scans a profile page. The form has a hidden field named csrf_token, and the server accepts a post only if the value matches the one issued with the form. Their first scan reports no findings on that page. The scan log tells another story.
| Step | What the log shows |
|---|---|
| 1 | Scanner loads the form and records a token value |
| 2 | Scanner sends 400 variations of the name field, each carrying that same old token |
| 3 | The server rejects every one with a 403 error and a generic message |
| 4 | The report says "no issues", though no payload reached the handler |
The team then takes five steps.
- They confirm the token field name and add it to the scanner's list of token names, since the project uses a custom name.
- They configure a success check, a text string that appears only after a successful save.
- They run a short scan with one thread and watch the log. Now most requests return 200 with the success text.
- They raise the thread count and see failures return, which shows the tokens are single use. They keep one thread for this area.
- They rerun the full scan. It finds a stored input problem in the name field that the first scan never reached.
The responses and counts in this example are illustrative. The pattern is common and easy to check.
Variations in token design
Tokens are not all alike. The way a token is issued changes how a scanner must handle it.
| Design | How it behaves | Scanner approach |
|---|---|---|
| Hidden form field, new per page load | Changes every time the form loads | Fetch the form before each submission |
| Per session token | Stays the same until logout | Capture once, reuse |
| Header token read from a cookie | Copied by front end code | Needs script or browser-driven handling |
| Token inside a single-page app API call | Issued by an earlier API response | Use a recorded login and call sequence |
| Same-site cookie settings only | No token at all | Nothing to handle, but test cross-site behavior |
Think about what the scanner can learn from CSRF protection
A scanner that handles tokens can still test whether your protection works. A good manual check is to remove the token, send a request, and confirm it is rejected. Then use a token from another session and confirm that fails too. If either succeeds, the protection is weaker than it looks, and that is a real finding.
Document your setup
Write down the token names, the success text and the thread count. The next person who changes the form will otherwise break the scan without knowing it. A one-page note beside the scan config is enough.
When to ask for developer help
If the front end builds tokens in script and no list of names works, ask a developer for a test mode. A common arrangement is a flag that turns token checks into logging only, available for a dedicated scan account in a non-production environment. Record that the flag exists and that it must never be enabled in production.
Where Safeguard fits
Token handling is a web testing concern. For the libraries behind your forms, Safeguard offers SBOM and dependency scanning with reachability analysis, and a free scan at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen.
Watch for steps where a form is submitted and think about how a fresh token would be obtained.
Quick check
How can I tell the scanner never reached my form handler?
Look for many 403 or 400 responses on POST requests in the scan log.
Should I disable CSRF checks in staging for scans?
Avoid it. Teach the scanner the token name, or relax it for one test account only.
Do SameSite cookies replace anti-CSRF tokens for scanning purposes?
They change what a scanner must handle, since there may be no token to fetch. Still test cross-site behavior, because the cookie setting alone does not cover every case.
Sources
Last reviewed: 19 October 2026.