We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF) happens when a browser is tricked into making a request the user didn't intend, using credentials the browser automatically includes.

Unlike XSS, CSRF does not require code execution in the target application. An attacker only needs a state-changing endpoint that trusts a session cookie:

<form action="https://bank.example.com/transfers" method="post">
  <input type="hidden" name="recipient" value="attacker" />
  <input type="hidden" name="amount" value="500" />
  <button>Claim 500 free gems</button>
</form>

If a signed-in customer submits that form from an attacker-controlled page, the browser may attach the bank's session cookie! Sure, the attacker can't read the response, but they don't need to: the unauthorized transfer has already happened.

Same-Site Is Not Same-Origin

https://shop.example.com and https://evil.example.com are cross-origin because an origin includes the exact host. They're still same-site because a site uses the scheme and registrable domain. Different ports on the same host are also cross-origin but same-site.

That distinction matters because SameSite cookies govern cross-site requests, not every cross-origin request. They're an important defense, but an attacker-controlled sibling subdomain can still be same-site.

Check the Request Source

For state-changing browser requests, the server can compare the request's Origin header with its trusted application origin:

Origin: https://evil.example.com

If Origin is present, it must match exactly. When it's absent, the server can parse the Referer header and compare that URL's origin instead. Prefix or substring checks are unsafe: https://shop.example.com.attacker.example is not https://shop.example.com.

If neither header is present, the server can't verify the request's source at all and should reject it. Sure, that might reject some non-browser clients that send neither header, but treating an unverifiable request as trusted would leave the hole wide open.

Assignment

Bearly Secure needs to verify that state-changing requests came from its own pages. Build the request-source guard before allowing POST requests through.

  1. npm run attacker-lab
    

With Bearly Secure and Bearly Evil still running, run and submit the CLI tests from the project root.

You can stop Bearly Evil after successfully submitting this lesson.