Safeguard
Application Security•

ASVS 5.0 V14.2.1: No Sensitive Data in URLs or Queries

ASVS 5.0 V14.2.1 (Level 1): send secrets in the body or headers, never the URL. Code fixes, a log search test and mistakes to avoid.

Safeguard Security Team
3 min read

ASVS 5.0 V14.2.1 is a Level 1 requirement. Sensitive data must travel to the server only in the HTTP message body or in header fields. The URL path and the query string must never carry things like an API key or a session token.

URLs get copied, logged and shared. Bodies and headers mostly do not.

Why it matters

A full URL ends up in many places that were never designed to hold secrets:

  • web server and proxy access logs
  • browser history and bookmarks
  • the Referer header sent to other sites
  • monitoring tools and analytics
  • screenshots and chat messages

If https://app.example.com/export?api_key=abc123 is pasted in a ticket, the key is now in a ticket system. HTTPS protects the URL in transit, but it does nothing about any of the places above.

How to implement it

Move credentials to a header:

// Bad
fetch(`/api/orders?token=${token}`);

// Good
fetch("/api/orders", {
  headers: { Authorization: `Bearer ${token}` },
});

Use POST with a body for sensitive input such as passwords, personal identifiers and search terms that reveal private facts:

@app.post("/lookup")
def lookup():
    national_id = request.json["national_id"]   # not in the query string

For flows that really need a link, such as password reset or email verification, use a single use, short lived token, invalidate it on first use, and send Referrer-Policy: no-referrer on the landing page so it does not leak onward.

Scrub at the edge as a safety net:

# Log path only, never the query string
log_format safe '$remote_addr "$request_method $uri" $status';
access_log /var/log/nginx/access.log safe;

Search your own code for URLs built with token=, key=, password=, secret= and session=. Secret scanners find these patterns in repositories; see the secrets scanning demo.

Click through it at your own pace, or open it full screen.

How to test it

  1. Browse the application with a proxy such as ZAP or Burp and filter requests whose URL contains token, key, auth, secret, password or session.
  2. Search access logs: grep -E "[?&](token|api_key|password|secret|session)=" access.log. Any hit is a finding.
  3. Check the Referer header sent to third party hosts from pages reached through a tokenized link.
  4. Read client side code for string concatenation into URLs.

Common mistakes

  • Putting the session id in the URL for "convenience" of a download link. Use a signed, short lived URL for that file only.
  • WebSocket connections that pass the auth token in the query string, because the browser API cannot set headers. Send it in the first message or use a short lived ticket instead.
  • Logging full request URLs in the application, even after the proxy was fixed.
  • Treating a short expiry as permission to put long lived credentials in a link.

Quick check

Is a one time reset token in a link a violation?

It is the accepted exception when the token is single use, short lived and handled with the controls above. A reusable API key in a link is a violation.

Does HTTPS make query strings safe?

No. It protects the transit, while logs, history and referrers still record the URL.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.