Cross Origin Request Sharing (CORS) is complicated, and that complexity creates a lot of places where security vulnerabilities can sneak in. This article will give you a “lite” overview of CORS, with a focus on security vulnerabilities that we see most frequently on tests. This isn’t intended to be a full CORS primer – if you’re interested in really diving in, there’s a resources section at the end that will help you get started.
CORS has many facets, so let’s cover a few of the more common cases one by one and see what can go wrong.
In all the following images, the big green thumbs-up means the scenario was a success and happened, and the big red thumbs-down means the browser refused to carry out the scenario (though it might not refuse until partway through – CORS is complicated and I’m skimming over some stuff here).
The Access-Control-Allow-Origin header doesn’t usually open up security problems on its own, but it can make other problems much worse when it’s added.
If a request to example.com returns an Access-Control-Allow-Origin: foo.bar header and an Access-Control-Allow-Credentials: true header, then any scripts on foo.bar execute with privileges as if they were native scripts on example.com. This means that XSS vulnerabilities on foo.bar are equivalent to XSS vulnerabilities on example.com! If you’re the owner of example.com, that’s a lot of trust to place in the owner of another site, and you should think about it very carefully before you do.
There are a couple of protections in place to prevent accidents around the Access-Control-Allow-Credentials header because it’s so dangerous. For one, it cannot be combined with Access-Control-Allow-Origin: *. Some sites get around this by just reflecting back whatever is sent in the Origin header in the Access-Control-Allow-Origin header in their response, bypassing this restriction. If you ever contemplate doing such a thing, please think very carefully about the risk you’re taking on before you do. Another restriction on the Access-Control-Allow-Credentials header is on the client side – XMLHttpRequests must deliberately set a “withCredentials” flag in order to request that credentials be sent, to make sure you absolutely want the browser to send cookies and other sensitive material with your cross-domain request.
These protections are kind of confusing, so I’ve set up a grid below that shows the different scenarios you may find yourself in.
Some applications attempt to split the difference, and validate the origin header manually instead of just reflecting it – so that e.g. anything matching “*.example.com” is considered “safe” and will be returned in the Access-Control-Allow-Origin header.
Do you see the problem here? An attacker who can convince a user to visit “example.com.malicious.com” (likely through phishing) can conduct the same attacks as before, because that site matches the pattern! This is still better than reflecting back any arbitrary origin in the Access-Control-Allow-Origin header, but as always, validation is very tricky to get right.
There are other twists and turns to CORS, and headers we haven’t covered, but the key that you need to understand is this: CORS is complicated, and any time you’re considering doing anything with it, it’s worth doing some research into the implications. The resources below are things I’ve found useful in understanding the security implications of CORS, and if you’re the hands-on type you should definitely set up the CORS test server and poke at it for a while to help you understand what’s going on.
Good luck, and I hope you enjoyed this CORS lite!