Auth tokens placed in URL query parameters (CWE-598, CWE-200) are automatically recorded in browser history, server access logs, proxy logs, and the HTTP Referer header sent to any third-party resource on the next page. A token that lives in a URL has already been logged somewhere beyond your control. OWASP A02 (Cryptographic Failures) covers sensitive data exposure through insecure transport, and OWASP ASVS V7 Session Management explicitly disallows placing session identifiers in URL query strings; a token visible in a URL is effectively broadcast to every system the request passes through.
High because URL-embedded tokens are passively harvested from server logs and Referer headers without any active attack against the user.
Move every persistent auth token out of URLs and into request headers:
// Use the Authorization header, not a query string
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` }
})
// Never do this:
const response = await fetch(`/api/data?token=${token}`)
For server-to-server calls, use an Authorization header or a dedicated custom header like X-API-Key. The only accepted exception is a one-time-use, short-lived token in an email link (password reset, email verification) — but even these should expire within 1 hour and be invalidated immediately on first use.
ID: saas-authentication.session-management.no-auth-tokens-in-url
Severity: high
What to look for: Search for patterns where auth tokens, session IDs, or sensitive credentials appear in URL query parameters. Check API routes for ?token=, ?session=, ?auth=, ?key= patterns in redirect URLs, link generation code, or API client code. Check OAuth callback handling to confirm the authorization code is exchanged server-side rather than forwarded. Also check email verification and password reset flows — tokens in links are acceptable for these one-time flows as long as they are time-limited and single-use. Count all instances found and enumerate each.
Pass criteria: Auth session tokens and permanent API keys are never passed in URL parameters. One-time tokens in email links (reset, verification) are acceptable as long as they are time-limited and invalidated after use. At least 1 implementation must be confirmed.
Fail criteria: Session tokens, permanent API credentials, or bearer tokens are passed as URL query parameters where they could be logged in server logs, browser history, or Referer headers.
Skip (N/A) when: No auth tokens or API key patterns found in the codebase. Signal: project has no API routes that accept tokens via query parameters.
Note: ?redirect= and ?returnTo= parameters used for post-login navigation are NOT auth tokens and are out of scope for this check. Open redirect concerns are a separate security issue. Only flag URL parameters that contain session identifiers, API keys, JWTs, or other authentication/authorization credentials.
Detail on fail: "API client at lib/api.ts passes Authorization token as ?token= query parameter rather than in the Authorization header" or "Session ID is appended to redirect URL in auth callback, exposing it in Referer headers".
Remediation: URLs appear in browser history, server logs, proxy logs, and are sent in Referer headers — placing auth tokens there exposes them broadly. Always use headers:
// Correct: token in Authorization header
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` }
})
// Wrong: token in URL
const response = await fetch(`/api/data?token=${token}`) // Never do this
For API keys in webhooks or server-to-server calls, use the Authorization header or a custom header. The only acceptable token-in-URL pattern is a one-time-use, time-limited link sent via email.