Web Security 10 — CSRF
1 Intro
CSRF, Cross Site Request Forgery, is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request.
- Web Security 01 — Prepare
- Web Security 02 — Referrer
- Web Security 03 — X Powered By / Server
- Web Security 05 — X-Frame-Options
- Web Security 06 — CSP (Content Security Policy)
- Web Security 07 — XSS Protection
- Web Security 08 — Sniff
- Web Security 10 — CSRF
- Web Security 11 — CORS
- Web Security 12 — SQL Injection
2 Review Referrer Sample
In Web Security 02 — Referrer, we have given a simple example of How referrer can avoid basic attack. Actually, that sample is some kind of CSRF attack. Malicious website runs on 8889, and our blog system runs on 8888. There is an API in our blog system /api/transferPoints?dstUser=xxx
. When called after authenticated, it will transfer points to dstUser xxx
.
Below is the operations when user is not authenticated. It won’t cause any point loss.
But if a user is currently authenticated, points will be transferred after unconsciously click on the malicious link.
You can try this by checkout the 10CSRF
branch, and run:
npm i
npm start
npm run-script startHack
3 How to avoid CSRF attack
- Use only JSON APIs : AJAX calls use JavaScript and are CORS-restricted. There is no way for a simple
<form>
to sendJSON
, so by accepting only JSON, you eliminate the possibility of the above form. - Disable CORS : The first way to mitigate CSRF attacks is to disable cross-origin requests. If you’re going to allow CORS, only allow it on
OPTIONS, HEAD, GET
as they are not supposed to have side-effects. Unfortunately, this does not block the above request as it does not use JavaScript (so CORS is not applicable). - Check the referrer header : Unfortunately, checking the referrer header is a pain in the ass, but you could always block requests whose referrer headers are not from your site. This really isn’t worth the trouble. For example, you could not load sessions if the referrer header is not your server.
- GET should not have side effects : Make sure that none of your
GET
requests change any relevant data in your database. This is a very novice mistake to make and makes your app susceptible to more than just CSRF attacks. - Avoid using POST : Because
<form>
s can onlyGET
andPOST
, by using other methods likePUT
,PATCH
, andDELETE
, an attacker has fewer methods to attack your site. - Don’t use method override! : Many applications use method-override to use
PUT
,PATCH
, andDELETE
requests over a regular form. This, however, converts requests that were previously invulnerable vulnerable! Don’t usemethod-override
in your apps - just use AJAX! - Don’t support old browsers : Old browsers do not support CORS or security policies. By disabling support for older browsers (which more technologically-illiterate people use, who are more (easily) attacked), you minimize CSRF attack vectors.
- CSRF Tokens : Alas, the final solution is using CSRF tokens. How do CSRF tokens work? 1) Server sends the client a token. 2) Client submits a form with the token. 3) The server rejects the request if the token is invalid. An attacker would have to somehow get the CSRF token from your site, and they would have to use JavaScript to do so. Thus, if your site does not support CORS, then there’s no way for the attacker to get the CSRF token, eliminating the threat. Make sure CSRF tokens can not be accessed with AJAX! Don’t create a
/csrf
route just to grab a token, and especially don't support CORS on that route!
4 Sample of fix solutions
So there are many fix solutions:
1). Check change referrer : Web Security 02 — Referrer
2). Disable CORS : Web Security 11 — CORS
3). Change this API to post instead of using get, as this API will cause backend changes
4). CSRF Tokens
Now we will fix this problem in solution 3 and 4.
4.1 Change this API to post instead of using get
First let’s update our API from get
to post
. In backend indexPost.js
, we change API from get
to post
(read from body
instead of query
):
Also, we need to update UI as well (Ajax from get
to post
):
Start the app again:
npm run-script startHack
npm run-script startPost
When we click the malicious get URL , nothing happens because this is a post request.
However, hackers can also update their methods. Let’s also have an example:
Malicious guys can work around this!
4.2 CSRF Tokens
The point of CSRF tokens is that, we will use another token to verify the user’s identity. And for each post request, which could do modification to our backend, we will check CSRF token and refresh it. Many people think it is not necessary to refresh them every time. But we won’t talk about that here. We will only give a simple example.
Now every time, if a user logged, we will put a CSRF token in the session, and we will stored it. When we use post method in UI, backend will verify the token in session and from post body. If verified OK, we will refresh this token. And if not, we will redirect user to login page.
Now in our indexCSRF.js
, we add CRF related functions:
When we first logon
, if everything is OK, we will set CSRF. When user calls transferPoints
, we check CSRF:
What about UI? We should first store the token. staticFileSafeCSRF/index.html
:
And when we do post, we should do more things staticFileSafeCSRF/post.js
:
And now, start applications:
npm run-script startCSRF
npm run-script startHack
Yeah, even malicious post requests can’t success any more.
What is this place? Princess Margaret Bridge, Fredericton, NB, Canada.