What is Cross Site Request Forgery (CSRF) and how can it be avoided?

In simple terms, CSRF is misusing the "saved" trust a website has in your browser. Often your token for authentication will be stored in a cookie. When a browser creates a request for a website it automatically sends all persisted cookies for that website along with the request. This basically means that any request sent from your browser will appear as if it was from you. You would of course have to have logged onto or otherwise made some state (cookies) on the site at some point. But just imagine how rarely you have to log on to facebook, you are authenticated for a very long time on most sites.

Simply, a CSRF exploit uses the authenticated state in your browser to execute something unwanted. Such as a money transfer or posting without your consent.

I will go through some of the cases here.

How it can be done

The attacks come in many different variations. It can be as simple as a website misusing the HTTP GET verb. A GET request should be safe to use at all times - It should not change anything on the server side. w3 states that "the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe""

However it is up to the creator of the website to adhere to this standard. Meaning a GET request easily can be implemented as a POST or PUT - changing information on the server. All the information for a CSRF attack using GET requests must be within the url. Like the request below (totally made up):

Normally you would not be allowed to change my name like above. However if you somehow made me go to that link, and I was already logged in, then the get request would succeed without a problem - that is - if no protection against CSRF is in place. Instead of just changing a name it could have been much more serious like a money transfer or buying something online.

How would someone execute this? Well a simple technique would be to put the get request within the src of the image on a site or perhaps send it in an email. The browser would automatically try and request (GET) the image. Another attack vector would be a simple old-school http form with a post. However you would have to trick the person into submitting it - using social engineering of some sort.

Something to note is that the "attacker" will never get the HTTP response. Meaning he or she will not get the data from the responses. This is all about executing something on the server side in your name. For this to work there has to be two mistakes made 1) There has to be a site with a weakness (GET endpoint that changes state) and 2) Someone with malicious intent has to make someone mistakenly request it.

Counter measures

There are two common approaches to countering CSRF attacks, the first is using a CSRF token and the second is checking the origin and referer headers on requests. I will list them below.

The CSRF token

The CSRF token is often implemented as a unique key associated with a user's session. The key should never be saved or read from a cookie as that will just be sent to the server. It should be stored on the page of the site as a hidden field. When you make HTTP requests this token should accompany the request as a header (in some cases a query parameter will do).

Why is this more secure? Because the attacker will never have the CSRF token from the page. The attacker provides the link to the GET request, but the attacker will not have the CSRF token, it is only obtained from visiting the page as the logged in user. With this approach the URL is not reusable for all users as they all have different CSRF tokens. Without the CSRF token the request should be rejected. When the user actually browses the site the token should be added to the header if making the GET request.

Check the origin and referer header

You can assure that the request comes from the same origin (your website) by checking the origin and/or referer header. This header will contain the domain/url from where the request is sent from. Yes this header can be changed by scripting but there is no way to do so in the examples above (where a user clicks a link). In this approach you should deny requests which are not originating from your site.

Most browsers already implement this through the same origin policy.

Wrapping it up

CSRF is not that hard to counter. By not misusing the HTTP GET verb you will already be very far. But simple countermeasures like the two above can secure your users (yes not you, your users) against this type of attack.

I hope you found this helpful, feel free to leave a comment down below :)