What Is a CSRF Attack? A Beginner’s Guide to Understanding and Preventing It
If you’ve ever created a login form, a password change mechanism, or a method to delete an account, you may have encountered CSRF vulnerable features.
A CSRF vulnerability doesn’t mean your application will fail or raise an error. It means that it allows an attacker to make an authenticated user’s browser send a forged request to a site to which the user is currently authenticated.
Being a student of web security, CSRF was one of the first vulnerabilities to really shake my understanding of how websites authenticate. The shocking thing about it is that CSRF doesn’t usually need an attacker to steal a password or to hack a database directly. The vulnerability exploits browser authentication mechanisms, especially cookie handling.
In this guide, you’ll learn what CSRF is, how an attack is performed, why some popular defenses are not enough, and how you can protect your applications. The examples provided here are meant to be used only for educational and defensive purposes.
The Problem CSRF Exploits: Browsers Can Send Your Credentials Automatically
In order to appreciate the Cross-Site Request Forgery vulnerability, we must first discuss the mechanism used to log into an ordinary website. When one logs in on a website, the server may generate a session and give the browser a session cookie. On every other request that satisfies the criteria of the cookie, the browser will automatically append the cookie.
The server will use the session data to ascertain the identity of the user and to evaluate if the requested activity is authorized. This system is very convenient in the sense that you do not need to type your password every time you open another page on the website. But this is a potential security problem.
Consider the scenario where you are logged into bank.example but you then browse another website, attacker.example, without logging out from your bank website. The browser cannot figure out by itself whether you actually wanted to pay for something or modify a settings of your account. Such thing has to be made through the security mechanisms of the application. This is the essence of CSRF vulnerability.
Cross-Site Request Forgery happens when the attacker makes you submit an unintentional request to a site where you are logged in as an authenticated user, performing an action you have not wanted to perform. It should be noted that CSRF is not session cookie theft. The attacker may never get the value of the cookie.
However, CSRF depends on how the application handles authentication and processing of requests.
A Practical Example: The Unwanted Money Transfer
Consider a hypothetical banking website that transfers money through the following request:
GET /transfer?to=Bob&amount=100 HTTP/1.1
Host: bank.example
This approach is vulnerable due to the usage of GET in order to make the state-changing action.
According to HTTP protocol, GET is considered a safe method. In other words, clients must be allowed to make use of it without having to ask the server to change its application state. Developers should not use GET in order to perform actions like transferring money or closing an account. Let’s assume now that an attacker builds his own webpage with an image tag which refers to the above mentioned URL.
Once the visitor enters the attacker’s webpage, he will probably make an attempt to get an image from the banking site. And if the banking site endpoint transfers the money using GET with the user’s auth cookie, then the server will perform a transfer. The browser will show the broken image since the answer will not be the image. But it doesn’t mean that the server cannot process the request.
Significant limitation: While this example shows the inherent weakness, it does not imply that the attack will work on all contemporary banking websites. The use of cookie attributes like SameSite=Lax, CSRF tokens, and server-side request validation can stop the attack. Specifically, SameSite=Lax cookie attribute normally ensures that the cookie is not sent along with cross-site image requests.
This general idea is that the server should never consider the existence of a valid session cookie as an indication of user’s intention to do something.
Can an attacker use POST instead of GET?
Yes. It is possible for an attacker to use POST to submit a cross-site HTML form through a browser. That is why the conversion of GET into POST is a significant step towards CSRF protection but not enough on its own.
When it comes to normal HTML forms, a POST request can be sent by a browser even without permission to access the response from the website. The same origin policy normally will prevent an attacking website from accessing a protected response coming from another origin. But that does not mean that one cannot prevent the submission of a request.
That is what CSRF is all about.
Why Some Common Defenses Are Not Enough
1. Using POST for every state-changing action
Switching the state-changing GET request to POST is a significant security step, although POST requests alone do not provide protection against CSRF attacks.
The attacker can make the browser submit the form using the POST method to the endpoint vulnerable to CSRF attacks. The developers should apply proper HTTP methods and CSRF countermeasures.
2. Using a secret session cookie
The session cookie has to be non-predictable and properly secured. But, the browser will automatically submit it without exposing its content to the malicious website.
This is why a session cookie alone is not enough to prove that a state-modifying request was deliberately triggered by the user.
Here are some of the purposes of the Cookie attributes HttpOnly, Secure, and SameSite:
- HttpOnly prevents regular client-side JavaScript from accessing the cookie.
- Secure ensures that the cookie is only accessed over secure HTTPS connections.
- SameSite dictates when the cookie should be sent in specific cross-site scenarios.
These attributes improve security, but they are not interchangeable. In particular, HttpOnly does not itself prevent CSRF.
3. Relying on HTTPS alone
The protocol HTTPS secures the transmission of data and protects it from being read or changed by network attackers. HTTPS does not automatically protect against a hostile website forcing the browser to initiate a connection to a legitimate HTTPS website.
HTTPS is vital, but the application needs proper protection from fake requests.
4. Ignoring the Origin and Referer headers
Headers such as Origin and Referer will aid the server to identify the origin of the request.
For instance, a server will be able to identify if the origin of the request is consistent with the origin that the application expects and then reject the state-changing requests when they are inconsistent with expectations. It is important for this method of authentication to be employed carefully.
The validation of the origin that the application expects must be done accurately rather than relying on loose string matching. In case both Origin and Referer are unable to give trustworthy information, then the application should consider employing some other form of protection.
This method is not futile. Validation of origin is an acceptable method of defending against CSRF attacks.
5. Assuming that multi-step forms are automatically safe
A sensitive operation broken down into multiple operations is not necessarily safe from CSRF attacks. For instance, a procedure that involves the user selecting the payee and confirming payment is still vulnerable if the server can accept forged requests at each of those points.
All state-changing operations have to be secured. In addition, sensitive actions should involve re-authentication or confirmation.
The Main Defence: CSRF Tokens
One widely used defense is the synchronizer token pattern.
The idea is straightforward: the server requires an additional, unpredictable token that an attacking website cannot normally obtain.
Here is how the process works.
Step 1: Generate a token.
The server generates a secure, unpredictable token associated with the user’s session.
Step 2: Include the token in the page
The server includes the token in a legitimate form, usually as a hidden form field.
For example:
<form action="/account/email" method="POST">
<input
type="hidden"
name="csrf_token"
value="SERVER_GENERATED_TOKEN"
>
<label for="email">New email address</label>
<input
type="email"
id="email"
name="email"
required
>
<button type="submit">Update email</button>
</form>
The example token is a placeholder. A real application must generate a secure token and provide the expected value through its CSRF protection mechanism.
Step 3: Validate the token on the server
When the form is submitted, the server checks the submitted token against the expected token associated with the user’s session.
If the token is missing or invalid, the server rejects the request.
Why does this help?
A malicious website may typically get a browser to send a form submission to a different website, but the same-origin policy would generally prevent the JavaScript on the attacking site from accessing the page at the good site, which contains the necessary token. The request, without the token, will fail the server-side test for Cross-Site Request Forgery.
For the synchronizer token pattern to work, the token needs to be generated unpredictably, verified on the server, and sent in a manner that is not automatically included by the browser with any cross-site request.
Putting the same secret into a cookie and verifying the existence of that cookie does not fulfill the synchronizer token pattern.
Use your framework’s built-in protection
You generally should not need to invent a CSRF token system from scratch.
Popular web frameworks provide established mechanisms:
- Django: Uses the {% csrf_token %} tag along with CSRF Middleware.
- Spring Security: Offers CSRF protection and allows for adding tokens to HTML forms or suitable HTTP Headers.
- Ruby on Rails: Includes mechanisms for the inclusion of authenticity-token for suitable requests.
These may vary based on the version of the framework and the architecture of the application.
In case of a server-side rendered application, use the built-in protections from the framework and make sure that the state-changing endpoints are covered. Do not remove CSRF protection due to some error in your form or API request. Figure out why the token is missing or incorrect.
A More Modern Defence: SameSite Cookies
SameSite attribute determines if a cookie is transmitted along with a cross-site request or not.
The possible values of SameSite are Strict, Lax, and None.
SameSite=Strict
The SameSite=Strict attribute prevents sending the cookie in cross-site browsing contexts from the browser.
Such a solution provides robust protection from cookie-based CSRF attacks, though it might have usability issues. For instance, the user coming through an external link to your site will initially lack the session cookie.
SameSite=Lax
Lax SameSite allows cookies for same-site requests and some cross-site top-level navigations with safe HTTP methods like GET. In general, it does not allow cookies in cross-site POST requests and embedded requests, which include image requests, among others.
Thus, Lax is helpful for many session cookies as the default setting, but it is not enough for protecting against CSRF attacks in applications.
This is because Lax cookie can be used in a cross-site top-level navigation, even for a state-changing GET request. It shows once again how important it is that GET requests should not alter the application’s state.
SameSite=None
A SameSite=None value will allow cookies to be transmitted even across sites, but it depends on other security restrictions by the browser. For this to work, cookies should also be secure in current browsers that are compliant.
It may be necessary in some cross-site integrations, but it increases the significance of having proper CSRF measures in place.
What should developers choose?
For many uses cases, making session cookies same site lax is a good starting point. However, using strict mode for the application might work if the application’s workflow supports it.
But cookie settings should be based on the needs of the application, and same-site should usually be used alongside other CSRF protection techniques.
Fetch Metadata: Using Browser Request Headers
Modern browsers can send HTTP request headers that describe the context in which a request was initiated. These are called Fetch Metadata headers.
One of the most useful is:
Sec-Fetch-Site: cross-site
Some possible values for the Sec-Fetch-Site header include:
- same origin
- same site
- cross site
- none
The header details the relation between the requesting site and the destination.
For instance, a browser request sent from a site unrelated to yours to your application may be sent as Sec-Fetch-Site: cross-site. The server may then use this data in rejecting cross-site requests intended to modify the application’s state.
A possible rule would be to reject such requests when Sec-Fetch-Site is set to cross-site and the request involves any of the following HTTP verbs: POST, PUT, PATCH, or DELETE, unless your application needs a particular reason to accept such requests.
This is just an example policy, and not a complete one intended for production environment.
Are Fetch Metadata headers enough?
Well, not necessarily. While Fetch Metadata might be supported by all the modern web browsers, older browsers, embedded environments, and non-browser clients may omit them.
A secure implementation must determine what to do about missing or unknown header values. OWASP suggests additional fallback mechanisms like origin verification or CSRF tokens when necessary.
It is also worth noting that there is a difference between site and origin. Two different subdomains may be same-site but yet different origins. So, if you trust same-site requests, you should ensure that your application’s subdomains are properly secured.
The advantage of Fetch Metadata is that it allows blocking many cross-site requests without sending any token with each one of them. But Fetch Metadata doesn’t make the use of authentication, authorisation, etc., unnecessary.
Where CSRF Still Surprises Developers
CSRF is not limited to traditional HTML forms. The risk depends on how authentication works and how the application accepts requests.
1. Login CSRF
Login CSRF is a less overt version of this type of attack. In an ordinary scenario, the attacker tries to get the victim to log into the web site through their own account.
The victim can then use the site thinking that it is his/her own account. Any information entered by the victim could be linked with the attacker’s account. Depending on how the application works, this could be a means for obtaining information from the victim.
Thus, login forms and authentication processes need some attention as well since protection should be applied to login requests as well as to actions done after successful authentication.
2. JSON APIs
Some developers assume that an API is automatically safe from CSRF because it accepts JSON. The reality is more nuanced.
A browser’s ordinary HTML form can submit certain content types without a CORS preflight. These include:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
A well-designed JSON API, which insists on application/json and refuses all other incompatible content types, is more immune to standard form-based CSRF attacks.
But issues will surface if there is some content type, which the endpoint mistakenly accepts, processes form data as JSON and allows for generous cross-origin policies.
In case of browser-based APIs with cookie authentication, one needs to ensure that the proper content type is required, along with suitable CSRF defenses, like validated tokens or customized request headers.
CORS policies need to be set right, since allowing credentialed requests from an untrusted source adds security vulnerabilities.
3. Stateless applications
A stateless application is not automatically safe from CSRF attacks. For instance, the server might not use the classical server session model but can still validate incoming requests via a token stored in a cookie.
Since the cookie will be attached by the browser automatically, the application can still be vulnerable to CSRF.
What needs to be considered here is not whether the server maintains any session. The important question is whether the browser will provide authentication credentials automatically for an attacker-initiated request.
4. APIs that use bearer tokens
Bearer token-based APIs that explicitly use this token in the Authorization header are usually less vulnerable to classical CSRF attacks in case the browser does not automatically send this token with cross-site requests. In particular, a JavaScript application may receive a token and explicitly use it when making an API request.
In contrast to cookie-based authentication, where a browser automatically sends the cookie. Nevertheless, bearer token-based authentication is not a guarantee of application security. Such issues as XSS, token stealing, storing tokens securely, and configuring CORS policy correctly should be considered carefully.
It is important to consider both the chosen authentication approach and storage and transmission of the credentials.
A Practical CSRF Prevention Checklist
If you are developing a web application, use this checklist as a starting point.
For server-rendered forms
- Leverage CSRF protection capabilities within your framework.
- Ensure the inclusion of the mandatory token in protected forms.
- Perform token validation on the server-side.
- Protect state-changing actions like account modifications or deletions.
- Do not grant exemptions for CSRF protection to any endpoints without a valid justification.
For session cookies
- Use Set Secure where the application uses HTTPS.
- Use Set HttpOnly where session cookies do not have to be accessible via JavaScript.
- Specify SameSite=Lax or SameSite=Strict depending on the needs of your application.
- Check whether there are cross-site integrations where another cookie policy may be needed.
- Keep in mind that these attributes perform different functions and cannot be considered equal.
For state-changing requests
- Do not use GET methods where application state can be modified.
- Check authentication on the server side for each sensitive operation.
- Protect against CSRF attacks with proper protection on cookie-based authentication.
- Verify Origin header and Referer header for cross-site request validation.
- Reject unexpected cross-site requests with Fetch Metadata, while providing a safe fallback for clients not sending the headers.
For APIs
- Ensure compliance with expected content types instead of accepting any request format.
- Set up CORS settings to allow only origins and credentials needed for the application.
- Guard browser-accessible APIs that employ cookies against CSRF attacks.
- Do authorization verification regardless of whether CSRF check succeeds.
- Evaluate the risks of token usage and transfer independently.
For sensitive operations
Some actions, such as changing a password, changing a recovery email, or transferring money, may require additional safeguards. Depending on the risk, consider reauthentication, transaction confirmation, or other controls that help ensure the action is authorised.
CSRF protection is only one part of securing a sensitive operation.
What I Have and Have Not Verified
The following guide describes the basics of CSRF attack and some defensive approaches from security literature such as OWASP, MDN, and popular web frameworks.
The examples provided above serve the illustrative purpose only and are not claims of successful exploitation of any website in question. I did not conduct penetration testing of a deployed application and have not validated specific implementation in all browsers and versions of web framework.
If you implement these measures, please refer to your framework documentation and test your application’s authentication flow, cookies configuration, and CSRF protection in a proper environment. In terms of additional reading, one may consider OWASP CSRF Prevention Cheat Sheet and MDN Web Security.
Conclusion
Cross Site Request Forgery (CSRF) is a form of web security weakness that occurs when an application receives unwanted authenticated requests without verifying whether they have been generated by an expected interaction. Browsers may automatically include cookies along with the request, which is good for session management, but it may also become an entry point for forging requests. The most critical thing here is that authentication does not equate to user intent.
Use CSRF protections provided by your framework, make sure that there are no state-changing GET requests, set up session cookies properly, and also think about origin verification and Fetch Metadata as extra levels of defense. In case of cookie-authenticated API, use the same level of thoughtfulness as you do for standard forms. In the case of bearer-token API, think about where and how the token is attached to the request.
There is no foolproof way to be completely safe. Nonetheless, with the correct implementation of CSRF protection methods together with proper authentication and authorization measures, along with good application design, the threat can be significantly mitigated.
First and foremost, follow your framework’s guidance for protecting yourself against CSRF attacks, learn why it works and make sure that your application denies fake state changing requests. This is far better than assuming that a website is safe simply because its forms work.
Also Explore Our Cybersecurity Category. And if you are reading it up to here, leave a sweet comment to motivate us to write blog everyday.


