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
Refererheader 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
- Browse the application with a proxy such as ZAP or Burp and filter requests whose URL contains
token,key,auth,secret,passwordorsession. - Search access logs:
grep -E "[?&](token|api_key|password|secret|session)=" access.log. Any hit is a finding. - Check the
Refererheader sent to third party hosts from pages reached through a tokenized link. - 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.