SourCode a FreeFungi app

Challenges FF-016

Sherbet CWE-352 A01:2021

Return to Sender

CSRF on the change-email action.

Your account lets you change your email address. The shop works out who you are purely from your session cookie, which your browser attaches automatically to every request to the site. There is nothing else proving you asked for the change: no secret token, no confirmation. So if an attacker can get your logged-in browser to make that request, the shop happily changes your email for them. This is cross-site request forgery.

New to this? What is CSRF?

Cross-site request forgery (CSRF) abuses the fact that your browser sends your cookies to a site on every request, even ones triggered by another website. If a state-changing action is protected only by that cookie, an attacker’s page can quietly make your browser perform it while you are logged in.

The defence is a secret the attacker cannot guess: a per-session CSRF token tied to the form, checked on submit. No token, no change.

Make sure the shop is running at http://localhost:3000, and be logged in as a grey-box account in the same browser.

1 · Find it

Log in, go to your account page, and change your email address. Watch what the browser sends: the new address travels straight in the URL in your address bar, with nothing else attached:

http://localhost:3000/account/email/change?to=me@example.com

No token, no confirmation, and it changes account state on a plain GET. Everything an attacker needs to forge it is right there in the URL.

2 · Exploit it

Play both roles yourself. Copy the HTML below into a new file called win-free-sweets.html anywhere on your computer (this is the trap page an attacker would host). It has no visible form: the script submits it the instant the page loads.

<h1>You have won a free bag of sweets!</h1>
<form action="http://localhost:3000/account/email/change" method="GET">
  <input type="hidden" name="to" value="attacker@evil.example">
</form>
<script>document.forms[0].submit()</script>

Now, while still logged in, double-click that file to open it in the same browser. Your browser attaches your session cookie automatically, the request fires, and your account email is silently changed to attacker@evil.example. Reload your account page to see it. A quick “forgot password” later and the account is gone. You never typed a thing.

Why it matters: CSRF turns any state-changing action into something an attacker can trigger, changing details, spending money, placing orders, all as the victim, just by getting them to open a page.

3 · Understand it

The handler updates the email using only the session, with no token to check:

app.get('/account/email/change', requireAuth, (req, res) => {
  updateEmail(req.user.id, req.query.to);   // no CSRF token, and on a GET
  res.redirect('/account');
});

Being logged in is not the same as having meant to make this request. The cookie proves who you are, not that you clicked the button.

4 · Fix it

Use POST for changes, and require a secret token that is issued with the form and checked on submit, so a forged request from another site cannot include it:

✗ Vulnerable
app.get('/account/email/change',
  requireAuth,
  (req, res) => updateEmail(...));
✓ Patched
app.post('/account/email',
  requireAuth, csrf(),   // token issued in the form, checked here
  (req, res) => updateEmail(...));

Keep cookies SameSite=Lax or Strict as a second layer, and re-ask for the password on sensitive changes like email. The token is the real fix; SameSite is the backstop.

The general rule: every state-changing request needs proof it was intended, not just proof of who you are. Use POST and an anti-CSRF token (most frameworks ship one), and never change state on a GET.