ASVS 5.0 requirement V3.5.3 says requests to sensitive functionality must use appropriate HTTP methods such as POST, PUT, PATCH or DELETE, and not the "safe" methods GET, HEAD or OPTIONS. As an alternative, you can strictly validate Sec-Fetch-* request headers to make sure the request did not come from an inappropriate cross-origin call, a navigation, or a resource load. It is a Level 1 requirement.
Why it matters
Safe methods can be triggered by anything: an image tag, a link prefetch, a script source, a chat preview bot. If GET /account/delete removes the account, then <img src="https://app.example.com/account/delete"> on any page does it. CSRF defenses are usually applied to unsafe methods, so a state change on GET also skips them.
How to implement it
Register state changing routes only for unsafe methods:
app.post("/account/delete", requireAuth, csrfCheck, deleteAccount); // yes
// app.get("/account/delete", ...) // never
Django:
from django.views.decorators.http import require_POST
@require_POST
def delete_account(request): ...
Where a legacy route must stay on GET, validate fetch metadata:
function sameSiteOnly(req, res, next) {
const site = req.get("Sec-Fetch-Site");
const mode = req.get("Sec-Fetch-Mode");
if (site === "same-origin" && mode !== "navigate") return next();
res.status(403).end();
}
Browsers set Sec-Fetch-* and scripts cannot override them. Treat a missing header as a refusal for sensitive routes, or accept it only for clients you know do not send it.
How to test it
for m in GET HEAD OPTIONS; do
curl -s -o /dev/null -w "$m %{http_code}\n" -X $m https://app.example.com/account/delete -b cookies.txt
done
Expect 405 or 403, and verify nothing changed. Then crawl your route list for GET handlers that write data.
Common mistakes
Logout or unsubscribe by GET. Both are state changes.
Framework "any method" routes. Restrict methods explicitly.
Treating Sec-Fetch as optional. An absent header must not mean allowed.
Quick check
Is GET with a token in the URL acceptable?
It avoids forgery, but URL tokens leak through logs and Referer headers. Use POST.
Sources
- OWASP ASVS 5.0, requirement V3.5.3
- MDN: Sec-Fetch-Site