Skip to main content

Perfect! Let’s merge everything into a final, complete, and structured reference for identifying, extracting, and injecting CSRF / anti-forgery tokens in ZAP for form-based authentication, in a way that’s easy to use and reference. I’ll make it clear, framework-specific, and also include universal patterns and practical ZAP usage.


Form-Based Authentication — CSRF / Anti-Forgery Tokens in ZAP

Form-based login requires dynamic handling of CSRF or anti-forgery tokens to prevent login failures. Modern web apps often embed these tokens in hidden fields, meta tags, or cookies. ZAP can detect and inject them automatically if configured correctly.


1. General bodyTemplate Pattern

The POST request body generally looks like this:

username={identifier}&password={password}&Login=Login&<token_field>={%token_placeholder%}
  • {identifier} → username/email placeholder
  • {password} → password placeholder
  • <token_field> → the name attribute of the hidden CSRF/anti-forgery input
  • {%token_placeholder%} → ZAP variable that dynamically injects the token

2. CSRF Token Field Names by Framework

Framework / AppHidden Field / Meta / CookieZAP Placeholder ExampleSample Sites
Ruby on Rails<input type="hidden" name="authenticity_token">
<meta name="csrf-token">
{%authenticity_token%}github.com, gitlab.com, basecamp.com, shopify.com (admin)
Django<input type="hidden" name="csrfmiddlewaretoken">
Cookie csrftoken
{%csrfmiddlewaretoken%}disqus.com, instagram.com (older), eventbrite.com
ASP.NET / MVC<input type="hidden" name="__RequestVerificationToken">
Cookie .AspNetCore.Antiforgery.*
{%__RequestVerificationToken%}microsoft.com portals, stackoverflow.com (partial), enterprise apps
Laravel / Symfony / CodeIgniter<input type="hidden" name="_token"> / <input name="csrf_token"> / <input name="CSRFToken"> / <input name="csrf_test_name">
Cookie XSRF-TOKEN
{%_token%}, {%csrf_token%}, {%CSRFToken%}, {%csrf_test_name%}laracasts.com, symfony.com
Spring MVC (Java)<input type="hidden" name="_csrf">
<meta name="_csrf">
Cookie XSRF-TOKEN
{%_csrf%}spring.io, enterprise Java apps
PHP (DVWA / Generic)<input type="hidden" name="user_token"> / token / csrf / _token / nonce{%user_token%} / {%token%}DVWA, generic PHP apps
WordPress<input name="_wpnonce"> / <input name="wp_nonce">{%_wpnonce%}self-hosted WordPress

3. Universal Patterns to Look For

Hidden Input Names to Grep:

authenticity_token        → Rails
csrfmiddlewaretoken → Django
__RequestVerificationToken → ASP.NET
_token → Laravel
csrf_token → Symfony / generic
_csrf → Spring
user_token → DVWA / generic PHP
CSRFToken → CodeIgniter
_wpnonce → WordPress
token → generic fallback
nonce → generic fallback

Meta Tags to Grep:

<meta name="csrf-token" → Rails
<meta name="_csrf" → Spring

Cookie Names to Grep:

csrftoken                 → Django
XSRF-TOKEN → Laravel / Angular default
.AspNetCore.Antiforgery → ASP.NET Core

4. How to Identify Unknown Frameworks

  1. View page source → Ctrl+U or browser “View Source”.
  2. Search for type="hidden" in the <form> element.
  3. Look at the name attribute of hidden inputs near the username/password fields.
  4. Cross-check with the table above.

Alternative using DevTools:

  • Open browser DevTools → Network tab
  • Submit the login form manually
  • Check the POST request payload: any extra hidden field besides username/password is likely a CSRF token
  • Check the login page cookies: look for csrftoken, XSRF-TOKEN, .AspNetCore.*

5. Form-Based Login BodyTemplate Examples

Framework / AppRequired Fields (Generic)Example bodyTemplateCSRF / Anti-Forgery Token Placeholder
DVWA / Generic PHPusername, password, optional Login buttonusername={identifier}&password={password}&Login=Login&user_token={%user_token%}user_token
Rails / GitHublogin, passwordlogin={identifier}&password={password}&authenticity_token={%authenticity_token%}authenticity_token
Django (Python)username, passwordusername={identifier}&password={password}&csrfmiddlewaretoken={%csrfmiddlewaretoken%}csrfmiddlewaretoken
ASP.NET / MVCusername, password, optional remember_meusername={identifier}&password={password}&__RequestVerificationToken={%__RequestVerificationToken%}&remember_me={%remember_me%}__RequestVerificationToken
Laravel / Symfonyemail, password, optional tenantemail={identifier}&password={password}&_token={%_token%}&tenant={%tenant%}_token
Spring MVC (Java)username, passwordusername={identifier}&password={password}&_csrf={%_csrf%}_csrf
WordPresslog, pwdlog={identifier}&pwd={password}&_wpnonce={%_wpnonce%}_wpnonce
Generic / Unknownusername/email, password, plus any hidden CSRF fieldsusername={identifier}&password={password}&<any_extra_field>={%<any_placeholder>%}&<csrf_field>={%csrf_token%}any hidden input detected dynamically

Notes:

  1. Dynamic placeholders:

    • {identifier} → your username/email
    • {password} → your password
    • {%<token_name>%} → dynamically extracted CSRF / anti-forgery token
    • {%<extra_field>%} → optional dynamic fields required by app
  2. CSRF / anti-forgery tokens are automatically detected by ZAP if added under Options → Anti CSRF Tokens. Examples:

user_token, authenticity_token, csrfmiddlewaretoken, __RequestVerificationToken, _token, _wpnonce, _csrf
  1. Generic approach: If the app has extra fields like remember_me, tenant, or domain, include them with {%<field_name>%} placeholders in the bodyTemplate.

  2. DVWA / simple PHP apps: Often only require username, password, and user_token.

  3. Complex / modern apps: May have multiple hidden tokens, redirect fields, or dynamic per-session values. Use the placeholder pattern for all of them.


5.2 Anti-CSRF Token Config in ZAP

  1. Go to Options → Anti CSRF Tokens
  2. Add the field name(s) ZAP should automatically detect, e.g.: authenticity_token, csrfmiddlewaretoken, __RequestVerificationToken, _token, user_token, _wpnonce
  3. ZAP will auto-extract and inject these during scans.

The stats.pscan.Anti CSRF Token Detection counter increments whenever ZAP spots a CSRF token, showing it’s actively handling them.


6. Example Full Form-Based Login (DVWA)

automation: true
scope:
entryUrls:
- "http://20.80.162.88:4280/"
includePaths:
- "http://20.80.162.88:4280.*"
excludePaths:
- "http://20.80.162.88:4280/logout.php"
- "http://20.80.162.88:4280/login.php"
- "http://20.80.162.88:4280/setup.php"
- "http://20.80.162.88:4280/security.php"
- "http://20.80.162.88:4280/vulnerabilities/csrf.*"

headers:
Cookie: "PHPSESSID={%cookie:PHPSESSID%}; security=low;"

authentication:
type: "form"
loginPageUrl: "http://20.80.162.88:4280/login.php"
loginBackendUrl: "http://20.80.162.88:4280/login.php"
bodyTemplate: "username={identifier}&password={password}&Login=Login&user_token={%user_token%}"
pollUrl: "http://20.80.162.88:4280/index.php"
loggedInRegex: "Welcome to Damn Vulnerable Web Application!"

Key Takeaways

  • Always reference the token field dynamically: {%<token_field_name>%}
  • ZAP handles extraction from hidden fields, meta tags, and cookies
  • Correct token injection ensures reliable login for DVWA, SPAs, Rails, Django, Laravel, .NET, and WordPress apps
  • Use pollUrl to verify login when login response alone isn’t sufficient

Got it! Here's a polished extra section on CSRF/anti-forgery token limitations for your ZAP documentation, integrating your Claude data and keeping it consistent with the style of your previous JSON/Form-based guides.


CSRF / Anti-Forgery Token Handling — Limitations in ZAP

ZAP is powerful for automating CSRF token detection and replay, but there are some limitations you should be aware of when using automation YAML for form-based authentication.


1. What ZAP Handles Automatically

ZAP natively detects and manages hidden form field tokens, such as:

  • Rails: authenticity_token
  • Django: csrfmiddlewaretoken
  • ASP.NET / MVC: __RequestVerificationToken
  • Spring: _csrf
  • Laravel / Symfony: _token
  • DVWA / generic PHP: user_token

Features:

  • ✅ Automatically extracts tokens from hidden fields in login forms.
  • ✅ Replays tokens in subsequent requests during crawling and scanning.
  • ✅ Counts detections in stats.pscan.Anti CSRF Token Detection.
  • ✅ Requires zero configuration in the automation YAML.

Example:

bodyTemplate: "username={identifier}&password={password}&user_token={%user_token%}"

ZAP replaces {%user_token%} dynamically at runtime.


2. What Requires Extra Work (Not Supported in Automation YAML)

Certain CSRF or anti-forgery token patterns cannot be expressed directly in YAML. These require inline httpsender Graal.js scripts:

CSRF PatternDescriptionStatus / Notes
Double submit cookieCookie contains token (csrftoken=abc123), but header (X-CSRFToken) must match it❌ Script only — cannot be done in YAML alone
Token in JSON script blobToken embedded in HTML <script type="application/json">{"csrf_token":"abc123"}</script>❌ Script only — extract from HTML, inject in request
Response JSON → request headerLogin response returns token ({"csrf_token":"abc123"}), next request requires it in header❌ Script only — intercept response, store, inject

Notes:

  • Inline scripting is possible but adds complexity.
  • These advanced patterns are outside standard automation YAML scope.
  • Focus on hidden form fields first; other patterns can be implemented later with scripts.

3. Summary Table

┌──────────────────────────────┬───────────────┬──────────────────────┐
│ CSRF Pattern │ ZAP Auto │ Status │
├──────────────────────────────┼───────────────┼──────────────────────┤
│ Hidden form field │ ✅ Native │ Fully supported │
│ (Rails, Django, .NET, Spring)│ │ │
├──────────────────────────────┼───────────────┼──────────────────────┤
│ Double submit cookie │ ❌ Script only│ Set aside │
│ (Angular, Instagram) │ │ │
├──────────────────────────────┼───────────────┼──────────────────────┤
│ JSON script blob in HTML │ ❌ Script only│ Set aside │
│ (Meta / Instagram apps) │ │ │
├──────────────────────────────┼───────────────┼──────────────────────┤
│ Response body → header │ ❌ Script only│ Set aside │
│ (custom APIs) │ │ │
└──────────────────────────────┴───────────────┴──────────────────────┘

⚠️ Recommendation: Start with hidden field token handling in YAML, then extend with inline scripts for double-submit, JSON, or response-to-header tokens as needed.