CSRF Token Present but Not Enforced on a Legacy JSON Endpoint
You are assessing an online bank at bank.example.com. The profile page changes email via POST /api/v2/profile/email with JSON and a valid anti-CSRF token in the body. The session cookie is set on .bank.example.com with SameSite=None; Secure because an embedded payment widget on partner sites needs it. You discover a legacy endpoint, POST /api/v1/profile/email, that accepts application/x-www-form-urlencoded, requires a logged-in session, and changes the email if the email parameter is present. It does not check the anti-CSRF token. It checks the Origin header only when the header is present; if Origin is missing, it proceeds. The v1 endpoint is still used by the mobile web view. The bank's bug bounty accepts CSRF findings only with a working proof of concept that changes state without user interaction beyond visiting a page.
1. Is this a valid CSRF finding? Explain why the token on the v2 form does not protect the v1 endpoint.
Read the worked answer
Yes, this is a valid CSRF finding if the proof of concept changes the email without any interaction beyond page load. CSRF protection is a server-side property of each state-changing request. A token on the v2 JSON endpoint protects only that endpoint; it says nothing about v1. The v1 endpoint accepts a simple form-encoded POST, which a browser can send cross-site without a CORS preflight. It does not require a custom header or a token. The session cookie has SameSite=None, so it is sent on the cross-site POST. The conditional Origin check is not a control: browsers normally send Origin on POST, but the server only checks it when present and does not reject a missing header. An attacker can craft a form that posts directly to v1.
The only remaining questions are whether the endpoint changes state and whether the new email can be controlled. The setup says it changes email if the email parameter is present, so the attacker sets that parameter to an address they control. The v2 token is a distraction and may create a false sense of security. The finding should be reported against v1 with the exact request and the cross-site HTML that triggers it.
2. Write the proof-of-concept HTML an attacker would host and explain each choice. What would make it fail?
Read the worked answer
A minimal PoC is an auto-submitting form:
<form action='https://bank.example.com/api/v1/profile/email' method='POST' id='f'>
<input name='email' value='[email protected]'>
</form>
<script>document.getElementById('f').submit()</script>
The form uses the default application/x-www-form-urlencoded encoding, which matches the legacy endpoint and is a simple request. The method is POST because the endpoint changes state. The single email parameter is the only field the endpoint needs. The script submits on page load so the victim does not need to click. The SameSite=None cookie is sent because the request is cross-site but the cookie explicitly allows it. The Origin header will normally be present and set to the attacker's site, but v1 does not reject a mismatched origin; if the header were missing, the endpoint also proceeds.
It would fail if the cookie were SameSite=Lax or Strict, if v1 required a valid anti-CSRF token, if v1 required a custom header such as X-Requested-With (which would trigger a preflight and fail without CORS approval), or if the server strictly validated Origin/Referer against an allowlist and rejected missing headers. A user interaction requirement, such as re-entering the password, would also prevent a pure CSRF proof.
3. Recommend a fix that addresses the root cause, not just the endpoint.
Read the worked answer
The root cause is inconsistent server-side enforcement across endpoints and an intentional SameSite=None cookie that removes the browser's default cross-site protection. Fix v1 by requiring the same anti-CSRF token as v2, validating it server-side on every state-changing request, and rejecting requests with missing or invalid tokens. Also validate Origin and Referer strictly against an allowlist, rejecting missing headers for state-changing requests. Remove or retire the legacy endpoint if the mobile web view no longer needs it.
Do not rely on SameSite alone. Because the payment widget requires SameSite=None, the bank needs token-based CSRF defense for all cookie-authenticated state changes. Consider requiring a custom header for API calls and enforcing CORS so cross-origin forms cannot reach the endpoint. For sensitive changes such as email, add re-authentication or a confirmation step. Add automated tests that enumerate all state-changing routes and assert token enforcement. The fix should cover profile, password, payee, and transfer endpoints, not just email, because the same legacy pattern likely exists elsewhere.