Skip to main content

ZAP Authentication — Lessons Learned

This section summarizes key best practices, common pitfalls, and debugging tips for authenticated scans in ZAP automation. Following these guidelines ensures more reliable scan execution and accurate authentication verification.


1. Host/URL Order

ZAP uses the first host in the hosts list as the primary domain for authentication verification:

hosts:
- https://login.example.com # loginPageUrl domain first ✅
- https://api.example.com # backend/API domain second

Rules:

  • Browser authloginPageUrl domain must be first
  • JSON/API authloginRequestUrl domain must be first

Incorrect order symptoms:

  • "Username field not identified" → loginPageUrl not first
  • Auth report shows wrong domain → login API domain not first

2. Authentication Method Selection

MethodUse CaseLimitations
jsonSPA/API apps with REST login returning tokenCannot handle client-side encryption, custom headers, complex redirects
formServer-rendered apps with HTML forms (PHP, Django, Rails)Session cookie based only, no token
browserComplex apps with RSA-encrypted passwords, SSO, OAuth, MFAHandles encryption, redirects, custom headers automatically

3. loginRequestBody Format

Always use a single-line JSON string. Multi-line bodies may get corrupted in afEnv.

# Correct
loginRequestBody: '{"loginId":"{%username%}","password":"{%password%}"}'

# Incorrect
loginRequestBody: |-
{
"loginId": "{%username%}",
"password": "{%password%}"
}

4. Custom Login Headers

loginRequestHeaders is ignored by ZAP. Use a Graal.js httpsender script to inject headers on login and subsequent requests:

// add-headers.js
function sendingRequest(msg, initiator, helper) {
msg.getRequestHeader().setHeader("customer-name", "ACME");
msg.getRequestHeader().setHeader("tenant-id", "1");
msg.getRequestHeader().setHeader("login-id", "user123");
}
function responseReceived(msg, initiator, helper) {}
jobs:
- name: injectHeaders
type: script
parameters:
action: add
type: httpsender
engine: Graal.js
name: AddHeaders
file: /zap/wrk/add-headers.js

5. Dynamic Token Extraction

ZAP only extracts JWT/Bearer tokens dynamically. Short strings or numeric values must be hardcoded in sessionManagement.

sessionManagement:
method: headers
parameters:
Authorization: Bearer {%json:results.accessToken%} # dynamic
tenant-id: "1" # static
login-id: "user123" # static

6. Verification Method

MethodUse CaseNotes
pollBackend API endpointPreferred for SPA, JSON, and browser auth
responseSpecific endpoint responseOptional for JSON auth only
autodetectAutomatic traffic analysisUnreliable; avoid for SPA and API auth

Rules for pollUrl:

  • Must be a GET endpoint
  • Should point to backend API, not SPA frontend route
  • Test with curl before using in ZAP

7. loggedOutRegex

  • Match unique error text in response body for failed authentication
  • Avoid matching HTTP status codes directly
loggedOutRegex: 'Invalid Credentials'

8. SPA Applications

  • Frontend SPA URLs (/#/dashboard) return empty HTML; never use for pollUrl
  • Backend API URLs return actual data → use for pollUrl and scan targets
  • Enterprise SPAs may require OpenAPI spec or HAR import for full coverage

9. Browser vs JSON Auth for RSA Encryption

  • Browser auth runs real JS encryption for each login
  • JSON auth only works if the encrypted string is static/reusable
  • Browser auth recommended for apps with client-side crypto

10. pollFrequency

  • Short scans → 10 seconds for quick re-auth and session verification
  • Long scans → increase as needed (30+ mins)

11. Memory and Container Sizing

Scan TypeContainerJVMCPU
Basic scan4G2G2
Advanced scan8G4G4
Browser scan12G6G4
  • Chrome runs outside JVM heap → needs ~4G additional memory
  • JVM heap should be ≤50–60% of container memory

12. Debugging Checklist

  1. Check afEnv in auth-report.json for parsed body and parameters

  2. Review stats.auth.detect.session for token extraction

  3. Test pollUrl manually with curl and required headers

  4. Inspect login requests in browser DevTools → headers and payload

  5. Review summaryItems failure reasons:

    • "No successful logins" → form not found or submit failed
    • "No indication of login" → regex mismatch
    • "Username not identified" → loginPageUrl domain not first