Receiving an email from a user who lost their account is not really surprising. But what about receiving this kind of email from 20% of your users? And what about also receiving complaints about fund transfers on your web application that weren't initiated by your users?
That is frightening!
This could happen if an attacker is able to impersonate your users and make mutations on their behalf (changing email address, transferring funds, etc).
How is it possible? Maybe with a CSRF attack on your web application or GraphQL API…
What is a CSRF?
CSRF is amongst the top three most common vulnerabilities in web applications and it can be really harmful. Cross-Site Request Forgery (CSRF) is an attack that forces a user to perform unwanted actions on a web application in which they are currently authenticated. These actions happen without the user even knowing it!
The initialisation of the attack begins with the user visiting another website (i.e. not the website vulnerable to CSRF). This website often belongs to the attacker and the attacker has used phishing or social engineering techniques to make the user visit it.
Why “request forgery”?
During the attack, a request to the vulnerable website is forged (i.e. created) by the user’s browser. As always, the browser will automatically include any credentials associated with the site (session cookie, IP address, …). Therefore, if the user is currently authenticated to the site, the site will have no way to distinguish between the forged request sent by the victim and a legitimate request sent by the victim.
CSRF attacks target functionality that causes a state change on the server (email address modification, password reset, purchase, fund transfer, …) because forcing the victim to retrieve data doesn’t benefit the attacker as only the victim will receive the response.
Let’s give an example!
Your website superwebsite.com has user accounts. Let’s say that after being authenticated, a user can change their account email by visiting the link email@example.com.
In addition, assume there is an attacker with its own website malicioussite.com and this website includes the following code:
<img src="https://firstname.lastname@example.org" />
Using some trick (phishing, social engineering, …), the attacker succeeds in making the user visit his website. They won’t see anything except a broken image, but their browser will fetch the image source link! So, if they are still logged in to your website, the action will be performed and their email will be changed.
- The blue part of the schema happens without the user even knowing it!
- If the user is an admin, the whole application is compromised and this can lead to all data being deleted or modified.
- This is a simple CSRF attack using a GET request but more complex ones can also do POST requests.
Attacks based on POST requests
GraphQL endpoints typically accept Content-Type headers set to
application/json only, which is wrongly believed to be invulnerable to CSRF. In fact, many middlewares may translate other formats (
form-urlencoded, …) into
If you want to catch CSRF and 100+ other GraphQL security vulnerabilities before it's too late, checkout Escape. Run hundreds of security scans in your CI/CD 🚀
First, security awareness helps reduce the likelihood and severity of CSRF attacks (e.g. being careful with phishing or using an admin account only when necessary). However, it is not enough.
A first step is to allow only POST requests to perform data-altering action. Obviously, this will prevent GET requests to be used as attack vectors. However it will still be possible to exploit CSRF based on POST requests, so you will have to implement other measures as well.
There exist several solutions to prevent CSRF, however, none of them is perfect. Let’s explain the more reliable and “simple” ones. (You can find more here).
One effective measure is to include a challenge for the user such as a CAPTCHA or requiring the re-entry of the user's password. When these techniques are properly implemented, they are fairly successful, however, it isn't always feasible to include a challenge in every request, and it can have a negative impact on user experience.
Synchronizer Token Pattern / CSRF token
Synchronizer token defences have been built into many frameworks. It is strongly recommended to research if the technology you are using has an option to achieve CSRF protection by default before trying to build your custom token generating system. You will still have to do token management but it will be much easier.
As explained in the following schema, the principle is to ask for a CSRF token before submitting the GET/POST request with it. Therefore the attacker doesn't have access to the CSRF token and cannot create a valid request (his attack skips steps 3 to 5 and goes directly to step 6).
- Don’t save the CSRF token as a cookie! Either include it as a hidden field in a form or in header parameters.
- CSRF token is unique per session.
Same Origin Policy
Modern browsers have mechanisms to prevent certain CSRF attacks by default, so make sure not to deactivate these protections. For example, don’t set the “Access-Control-Allow-Origin” header to “*”, otherwise you allow every website to make requests to your website.
Cross-Site Scripting (XSS) is another vulnerability and if you don’t protect your web application from it, CSRF mitigation techniques can almost all be defeated and the severity of the CSRF attack is amplified.
Learn more about XSS here: https://owasp.org/www-community/attacks/xss/
- Excellent explanation of CSRF: https://halls-of-valhalla.org/beta/articles/cross-site-request-forgery-demystified,47/
- OWASP: https://owasp.org/www-community/attacks/csrf
- OWASP mitigation cheat sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- Longer blog on GraphQL and CSRF: https://blog.doyensec.com/2021/05/20/graphql-csrf.html