Trick a user into performing an action they don't intend to perform. This could be:
- changing their account email to allow for password reset,
- changing content or users on the site, or
- transfering funds to the attacker.
For CSRF to be possible:
- There must be an action a user can be tricked into performing unintentionally,
- Only session cookies are used to validate the user's permission to perform the action; and
- The action must have predictable parameters (e.g. no CSRF tokens involved).
This HTTP request is vulnerable to CSRF:
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE
email=wiener@normal-user.com
The attacker can create a page with the following HTML page to cause the user to change their email address when a victim visits it:
<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>
For the above to work, the victim must be logged in to the vulnerable website and the session cookie is not using the SameSite
attribute.
An attribute set on session cookies that can have one of two values:
Strict
: the cookie is only sent with same-site requests.Lax
: the cookie is sent with same-site requests and top-levelGET
navigations (e.g. user clicking a link).
The risk with Lax
is that some sites will allow sensitive actions to be performed via GET
requests, even if not explicitly coded that way.
CSRF are delivery is similar to XSS and depends on the HTTP verb used to perform the action:
POST
: create an HTML form with the action and params and induce the victim to visit the page.GET
: create an image element with the malicious action as asrc
and have the victim visit the page:
<img src="https://vulnerable-website.com/email/change?email=pwned@evil-user.net">
There are several different ways to bypass CSRF defenses, depending on the site:
- CSRF not validated for
GET
requests. - CSRF not validated if token is missing.
- CSRF not tied to user session and only checked for validity. In this case, the attacker can use their own CSRF token in the attack.
- CSRF tied to a non-session cookie. Relies on the attacker being able to set a cookie on the victim's browser.
- CSRF is set in the cookie ("double submit cookie pattern") and the attacker can set the cookie on the victim's request.
Some sites attempt to prevent CSRF by checking the Referer
header. However this header can be spoofed or crafted in such a way to bypass the check.
# Bypass check where referer header must start with the vulnerable site domain
http://vulnerable-website.com.attacker-website.com/csrf-attack
# Bypass check were referer must appear in the header value
http://attacker-website.com/csrf-attack?vulnerable-website.com
💡 Modern browsers now strip query parameters from the Referer
header. You can override this by setting the Referrer-Policy: unsafe-url
header on your request.
Both attacks involve causing the victim to unintentionally perform an action:
- 'XSS': caues the victim to execute malicious JavaScript on the vulnerable site.
- 'CSRF': causes the victim to perform an action on the vulnerable site through visiting a malicious page.
CSRF is generally harder to exploit because most sites implement CSRF defenses and the attacks usually only apply to a subset of user actions.
CSRF are also only "one-way" in that they cause a user to submit a request, but the attacker cannot see the response directly. With XSS the attacker can see the response and can use it to further their attack.
Reflected XSS can be mitigated by CSRF tokens since they add a unique value to each request. But, CSRF tokens are not always validated for all requests and can be bypassed.
Stored XSS are not prevented by CSRF as they trigger when the user visits the page, which will include a valid CSRF token generated by the site.
- Use CSRF tokens with high entropy that are tied to the user's session.
- Use
SameSite
cookies. - Provide additional forms of verification on sensitve requests.
Unique, high-entropy random value submitted with a user's request. The server validates the token to ensure the request is not a CSRF attack. Additional context specific values (e.g. timestamp + salt) can be used to generate the token, which makes it harder to guess.
These tokens should be transmitted either as:
- a hidden field that appears before any user controllable content to prevent the attacker from inserting their own token into the request; or
- a header value since browsers prevent injecting custom headers on cross-domain requests.
<input type="hidden" name="csrf-token" value="CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz" />
Tokens should not be transmitted as GET
request parameters because:
- it will be logged in client/server-side logging,
- it is visible in the user's address bar, and
- it can be transmitted in the
Referer
header to third parties.