What is Cross Site Request Forgery?
Cross-Site Request Forgery (CSRF) is a type of attack that causes an authorized user to do undesirable activities on a web application. An attacker can deceive users of a web application into doing activities of the attacker's choosing using social engineering techniques (such as delivering a link or an image through email or chat). If the target is a regular user, a successful CSRF attack might drive them to make state-changing requests, such as transferring cash or altering their email address. CSRF can compromise the entire web application if the victim is an administrative account.
How does the attack work?
There are a number of ways in which users can be tricked into loading information from or submitting information to a web application. One of the most popular one is the URL link.
<a href="https:\/\/bankexample.com/transfer?account=john&amount=10000">You have won a Lottery!</a>
It could be a invisible fake image.
<img src="https:\/\/bankexample.com/transfer?account=john&amount=10000" width="0" height="0" border="0"/>
The zero by zero image will remains in an HTML page as an invisible image. The HTML page can be in an e-mail. When you open up this e-mail, you don't see the image but the browser or the e-mail application will still make the request.
That is the GET
request in which it will make the request automatically. On the other hand, the POST
request will require the vicitm to execute it. For example, by clicking a button. Since it is a POST
request, it cannot be delivered using stand A
or IMG
tags; it can be delivered using a FORM
tag.
<form action="https:\/\/bankexample.com/transfer" method="POST">
<input type="hidden" name="acct" value="john"/>
<input type="hidden" name="amount" value="10000"/>
<input type="submit" value="Won Lottery"/>
</form>
However, this also can be executed automatically using a JavaScript code.
<body onload="document.forms[0].submit()">
Similar CRSF attacks also be used in other HTTP methods, such as PUT
or DELETE
.
<script>
var request = new XMLHttpRequest();
request.open("PUT","https:\/\/bankexample.com/transfer",true);
request.setRequestHeader("Content-Type", "application/json");
request.send(JSON.stringify({"acct":"john", "amount":10000}));
</script>
The above code won't work because modern web browsers will block it due to same-origin policy
. Unless, the target website explicitly open up cross-origin requests by setting the following header.
Access-Control-Allow-Origin: *
Defense against cross site request forgery (CSRF)
Verification code
The most straightforward method of preventing CSRF attacks is to use verification code. CSRF attacks initiate network requests that users aren't aware of. In order to fulfill the final request, the user is required to interact with the program. As a result, CSRF attacks are prevented.
The verification code, however, is not a silver bullet. We cannot apply the verification code to all actions due to the users' experience. Therefore, it can only be one method of preventing CSRF, not the only one.
Referer check
Referer check may also be used to determine if the request is coming from a valid source. Even if we can check the referer to see whether the user has been attacked by CSRF, this will not be adequate to protect the user. The server's inability to take the referer all of the time is the weakness in referer check. For privacy considerations, many users limit the transmission of the referer. The referer is not always sent by the browser. For security concerns, the browser will not transmit the referer from HTTP to HTTPS.
Given these limitations, we cannot rely on referer check as a major way of CSRF defense, although it may be used to monitor a csrf attack.
Anti-CSRF Token
The usage of a token is another typical CSRF protection strategy. What is the reason for the success of a CSRF attack? The reason for this is that attackers can guess all of the critical operations' parameters. Attackers can only successfully create a fake request by knowing all URL parameters and their values; otherwise, the attack will not success.
As a result, a solution is to encrypt the parameter or use some random text to prevent an attacker from predicting the parameter values. Maintain the original parameters and add a new parameter, token. The value of the token is random and unpredictable.
For instance:
https:\/\/bankexample.com/transfer?account=john&amount=10000&token=[random]
<form action="https:\/\/bankexample.com/transfer" method="POST">
<input type="hidden" name="acct" value="john"/>
<input type="hidden" name="token" value="[random]" />
<input type="number" name="amount" value="10000"/>
<input type="submit" value="Transfer"/>
</form>
In the server side, validate the token. The tokens may stored in session or redis.
function (request, response) {
// Validate token against tokens in session or redis
if (!validate(request.body.token)) {
return response.status(400).end();
}
...
}
Tokens must be entered into both the form and the session at the same time. When a user submits a request, the server simply checks the token in the form against the user's session (or cookie) to see if the token is consistent. if it is, the request is considered legitimate; if not, or if the Token is null, the request is considered illegal, and may be CSRF attacks.
Token rules:
It should be random and unpredictable
Duplication is not an issue
It should have an expiration date
It should stored in server side session rather than cookie
It should be used in POST, PUT, DELETE method in a form or ajax. But definitey not GET method, in which it will leak token.
Do not put the token in the URL of a page, as the token may be disclosed by the referer header.
https:\/\/bankexample.com/account=abc&token=[random]
If the page contains a image, the token may be sent to evil.com
via referer header.
<img src='http:\/\/evil.com' />