What does that really mean? Can you please give me a real life example?
Attack example 1: Cross-Site Request Forgery (CSRF) with an HTML form
On page at
evil.com the attacker has put:
<form method="post" action="http://bank.com/trasfer">
<input type="hidden" name="to" value="ciro">
<input type="hidden" name="ammount" value="100000000">
<input type="submit" value="CLICK TO CLAIM YOUR PRIZE!!!">
Without further security measures, this would:
- the request does get sent. The SOP does not forbid this request from being sent.
- it includes authentication cookies from
bank.com which log you in
It is the synchronizer token pattern, alone, even without the SOP, prevents this from working.
Synchronizer token pattern
For every form on
bank.com, the developers generate a one time random sequence as a hidden parameter, and only accept the request if the server gets the parameter.
E.g., Rails' HTML helpers automatically add an
authenticity_token parameter to the HTML, so the legitimate form would look like:
<form action="http://bank.com/transfer" method="post">
<p><input type="hidden" name="authenticity_token"
<p><input type="hidden" name="to" value="ciro"></p>
<p><input type="hidden" name="ammount" value="100000000"></p>
<p><button type="submit">Send 100000000$ to Ciro.</button></p>
as mentioned at: https://stackoverflow.com/questions/941594/understanding-the-rails-authenticity-token/26895980#26895980
evil.com makes a post single request, he would never guess that token, and the server would reject the transaction!
See also: synchronizer token pattern at OWASP.
But then, what prevents the
- XHR GET for the token
- XHR POST containing the good token
evil.com would try something like this (jQuery because lazy):
// Parse HTML reply and extract token.
This is where the SOP comes into play. Although the
The Chromium developer console shows an error for it of type:
Access to XMLHttpRequest at 'http://bank.com' from origin 'http://evil.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Why not just not send cross request cookies instead?
What if implementations had a rule like: "allow any request, but only send cookies on current domain XHR"? Wouldn't that be simpler?
But that would still allow for another type of attack: when authentication is based not on cookies, but on source (IP) of the request!
For example, you are in your company's intranet and from there you can access an internal server, which is not visible from the outside and serves secret data.
Are all cross-origin requests forbidden?
Even forgetting CORS, no, we do them every day!
Cross-origin writes are typically allowed: links, redirects and form submissions.
[My comment]: e.g., when you click a link, you often expected to go logged in to the website, and that requires making an authenticated GET request that returns the new page.
Cross-origin reads are typically not allowed: XHR (example above),
However, read access is often leaked by embedding. For example you can read the width and height of an embedded image, the actions of an embedded script, or the availability of an embedded resource (and thus possibly if the user is logged in or not on a given domain)
In particular, it would be possible to use a form in
evil.com that makes an authenticated POST request to
bank.com. This is why the SOP alone is not enough: the synchronizer token is also needed.
See also: CSRF at OSWAP.
Other prevention approaches
- check if certain headers is present e.g.
- check the value of the
Origin header: Why is the synchronizer token pattern preferred over the origin header check to prevent CSRF
- re-authentication: ask user for password again. This should be done for every critical operation (bank login and money transfers, password changes in most websites), in case your site ever gets XSSed. The downside is that the user has to type the password multiple times, which is tiresome, and increases the chances of keylogging / shoulder surfing.