Cross-site request forgery vs Cross-site scripting
Both XSS and CSRF are client-side attacks.
Cross-site scripting
Let’s say the user is currently opening a browser tab in: mail.com
The browser stores user data specific to that website, e.g. cookie, localstorage…
In current tab, any js script can access those data.
Attacker tricks user to execute a script, that script will capture user data, and send to attacker site -> so “cross-site”
There is 2 important parts:
- Attacker tricks user to execute a malicious script
- By tricking user click to a url (either in chat, or email…)
- The url could be like:
mail.com/search?q="><script>...malicious code...</script>
- The script code can capture user data, and send to attacker site
- The script is excuted in current browser context (in domain
mail.com
). So it can access cookie or localstorage of that website. - With captured data, the script can http post them to attacker website.
- The script is excuted in current browser context (in domain
Solution
Just escape the user input (url, form submit url…). Any HTML script tag will become plain text, so it can’t be executed by browser.
Cross-site request forgery
“Request forgery” means copying/faking a request for malicious purpose.
Let’s say user is currently logged in to mail.com
.
A request to change password is like: mail.com/change-password?new=abc
Here, the attacker forges a request to change password, and tricks user to send it:
- Forge a request to change password to
xyz
- Just make an url:
mail.com/change-password?new=xyz
- Just make an url:
- Tricks user to click on that url
This is naive attack, only possible for bad designed API, and HTTP Get only.
Let’s consider a HTTP post example. Here is mail.com
form to change password:
<form method='POST' action='/change-password'>
<input type='text' name='new_password'/>
<input type='submit'/>
</form>
The attacker create a website: attacker.com
, has content:
<form name='fake' method='POST' action='http://mail.com/change-password'>
<input type='text' name='new_password' value='xyz'/>
<input type='submit'/>
</form>
<script>
document.onload = () => document.forms['fake'].submit()
</script>
Then, attacker tricks user to go to attacker.com
.
When page is loaded, the script kicks in, the form is submitted to mail.com
with new password.
Of course it is also naive implmentation. But it is possible.
Solution
For HTTP Post with form rendered by server-side:
- Whenever server renders a form, add anti-CSRF token field to form.
- The token is similar to session id, randomized for each form rendering request, .
- When form is submitted, the token go along with post request
- Server checks the CSRF token to match with saved token in server.
Because the token changes in every request, the attacker can’t know and can’t forge a valid request.