CSRF – Cross Site Request Forgery is an attack in which the user performs unauthorized actions on a web page they are signed in to. In this article I will describe the theory behind it and in next part (available here), I’ll give you some code to fool around with.
What is the problem?
Imagine you are logged into your blog, writing some article. You search for information, click some links, return to writing…
OK, what happened?
The browser automatically adds certain headers to requests like Origin, but also authorization headers from cookies. This is actually very convenient, because instead of sending you username and password (not to mention second factor passwords/codes/whatever) you send a token that can be easily reset. However, this means that if another page makes a request to any site you are logged in, this token will be sent as well. The attacker will never discover your password or this token, but nonetheless they will be able to perform some actions on your behalf.
Notice that because those tokens are sent with cookies, there is no CSRF attack vector if you don’t use them, which includes situation in REST APIs. Or, to be more accurate, if you do not use browser-provided mechanisms for authorization. You could still have CSRF attack if you authorized user by, say, user agent or IP address, which is not that likely, but you know – shit happens.
Step by step breakdown:
- the victim decides to visit malicious website,
- GET request to this sit is sent,
- browser sends this request to the malicious site including authentication cookies and the request is processed by the attacked site.
Bypassing example prevention method
OK, let’s take the DVWA medium-level protection as an example (I have not figured out yet how to do it on higher levels, maybe in next part I’ll do it). In this case the CSRF protection is conducted by this check:
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )
The problem is that
stripos searches for occurrences of
SERVER_NAME in whatever comes with
Referer header. The problem is that it contains entire path, so if th
SERVER_NAME points to
example.com and the user is tricked to visit
malicious.site/some/path/to/example.com the check would be successful.
The preferable way of dealing with this threat is using synchronizer tokens.
The web application adds token to form through hidden fields, or custom headers in case of AJAX requests, making this process transparent for the user, and sets CSRF cookie. Those both values are sent with request and need to match in order to pass the check.
You can compare this to how Django implements this mechanism here.
At this point while writing this article I started to think whether this is possible to read contents of the attacked page to get the token from form.
The answer is: it depends, but probably no. Unless the developer screws something up (I know that what I am writing is not very samrt). The reason for this is Same-origin policy. In general you can’t read data from other origin unless it’s somehow embedded – see MDN’s page linked in additional resources for more details.
That’s pretty much it, in next part of the article I am going to provide you with examples of secure and insecure examples of handling CSRF attacks, and maybe a solution to “hard” level of DVWA’s challenge.
- OWASP on CSRF prevention
- Django CSRF protection description
- Mozilla’s Developer Network about Same-origin policy