Most of web developers encounter at one point or another some errors about CORS in the console, of course, we look it up on Google, other devs tell us to just put a line in our backend, and then it’s all good from there …. Or is it?
What really happens when we add that magical StackOverflow one liner in our server
response.header("Access-Control-Allow-Origin", "*")
And how does it affect our security?
If we try to understand CORS, we find soon enough that we also need to understand SOP. And in this article we’ll try to understand how both of them work!
TL;DR
SOP and CORS are web security mechanisms that prevent unauthorised access to resources from different origins.
- Origin: Web content’s origin is defined by the scheme (protocol), hostname (domain), and port of the URL used to access it. Two objects have the same origin only when the scheme, hostname, and port all match.
- SOP: Same Origin Policy : SOP restricts web resources from being accessed across different origins
- CORS: Cross Origin Ressources Sharing : Allows web resources to be accessed across different origins with appropriate permissions.
Definitions
For the purpose of this introductory article, certain definitions are required, even though they may not be entirely precise and can be improved upon.
However, they adequately serve the intended scope of this article:
- Web Resource : A web resource is basically any resource that is the target of a web request.
- Origin : Origin is just a fancy word to talk about a tuple defined by the scheme, hostname, and port of a URL.
If two URLs have the same scheme, hostname, and port, they are considered to be from the same origin
Same-Origin Policy (SOP)
The Same-Origin Policy is a web security mechanism implemented on the browser side that restricts access to web resources
The purpose of SOP is to prevent malicious scripts from accessing sensitive resources by restricting them to only access resources from the same origin. This is because web browsers have the unfortunate tendency to send cookies and other informations to their corresponding origin on each request, which is quiet handy for all legit use cases, but can become problematic if a malicious person try to use this fact.
For example, imagine if client of https://bank.com
visits the malicious website https://evilWebsite.com
with the same browser he use to connect to his bank. And imagine that the evil website integrates malicious HTTP calls to https://bank.com
If there was no security mechanism, the malicious website will be able to just go ahead and read confidential banking information on behalf of the user, which is not good :
- User connects to
https://bank.com
- User visits
htttps://evilWebsite.com
evilWebsite.com
makes requests to confidential data inhttps://bank.com
- The browser sees a request to
https://bank.com
so he joins the cookies in the request - The bank server responds with the confidential data, and because there is no SOP, it’s forwarded to the evil website 😈.
But because of the Same-Origin Policy, the malicious website cannot directly access the user’s banking information since bank.com and evilwebsite.com have different origins.
However, SOP only prevents the malicious website from reading the answer, not making it. So, the malicious website can still make the request to the bank domain on behalf of the user, but it cannot read the response due to SOP.
That would need a different kind of protection to Cross Site Request Forgery, which may be the subject of a next article 👀.
In this exemple, the attacker makes the request to bank.com/pay?to=evil&amount=5000
and assuming it doesn’t need any kind of authentification or verification more than the cookies, the request will succeed.
Hopefully, most banks and websites will actually have CSRF protection and Multi Factor Authentication.
One common misconception is to believe that the SOP prohibits all cross-origin requests. If that were the case, content delivery networks (CDNs) and even the web would be worthless since it would be impossible to use images from other origins, or using javascript packages.
<img href="https://gravatar.com/my-avatar.com" />
Or
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
This is actually permitted in browsers with SOP !
Moreover, the SOP does not prohibit origins from contacting one another or sending and receiving requests (such as submitting forms), they only prohibit the website from reading the results.
But even with this set of rules, it is still too strict for some legitimate usage. For our case, our frontend https://ovrsea.com
will be able to make the HTTP Request to https://api.ovrsea.com
but will not be able to read the answer ( as they don’t have the same origin )
But thankfully, there is a way to allow us to tell the browser to chill out a bit : by using CORS !
Cross-Origin Resource Sharing (CORS)
Cross-Origin Resource Sharing is a web security mechanism that allows resources to be accessed from different origins if it is explicitly allowed to!
CORS works by adding HTTP headers ( one of them is the famous Access-Control-Allow-Origin
) to responses that indicate which cross origin is allowed to access a particular resource.
For example, if a web page from https://ovrsea.com
wants to access an API from https://api.ovrsea.com
, the API should tell the browser:
Hey it’s cool , ovrsea.com is a safe site for me, you can send him my data
And to do that technically the server of the api will set the header to allow for https://ovrsea.com
:
Access-Control-Allow-Origin: '<https://ovrsea.com>'
So now, we understand a bit more about what happens when we set ”Access-Control-Allow-Origin”: “*” :
It means that everybody is able to request the origin and read from it !
So if that’s not your intended purpose, you better restrict it !
By the way, if you ever wondered about the utility of the `OPTIONS` method request you see before making the actual request in your network inspector, well it’s because of CORS. It’s a preflight request that precedes the actual request, to ask the other origin if it’s okay to proceed with the actual request, or if it should be aborted.
So in the case of simple CSRF attacks like the one we saw before, the server would see that “evilWebsite.com” is trying to send money, and they will block it. But for it to actually fail, the bank server should not have ”Access-Control-Allow-Origin”: “*” !
There is also other headers that may be interesting to use that are more detailed here
Conclusion
SOP will restrict how we can share resources by limiting the readability of the resources, and CORS will relax those restriction to allow us to access them if they are explicitly allowed to !
But those protections and mechanisms are only implemented on the browser side, this doesn’t block requests made by other means like `curl`, `wget`, and any other request made by non browsers, so it shouldn’t be used as a way to restrict access to a backend, this should be done by other tools and mechanisms !