Thanks to the rapid growth of JavaScript frameworks such as Angular, Vue, and React, CORS has become a popular word in the developer’s vocabulary.
When requesting information from an external source such as an API (a pretty common practice for client-side JavaScript code), the origin of the resource must tell the web browser which domain, HTTP method, and header are allowed to request the resource.
This is possible and easy to do with the Cross-origin resource sharing (CORS) headers — the most common being Access-Control-Allow-Origin, Access-Control-Allow-Methods and Access-Control-Allow-Headers.
There are a couple other headers that aren’t security focused and would take a couple pages to explain all of them, so if you’re curious about them, don’t forget to check out the complete MDN guide on Cross-origin resource sharing (CORS) headers.
Access-Control-Allow-Origin
To allow an application hosted at https://sucuri.net to access a resource, you would need to specify:
Access-Control-Allow-Origin: https://sucuri.net
Unfortunately, it’s quite common to find applications that uses the following notation:
Access-Control-Allow-Origin: *
The wildcard symbol (*) will instruct the browser to allow access to the resource from any application. It’s highly unrecommended that you use that configuration unless your application serves only public content.
A good use case to demonstrate the potential of the Access-Control-Allow-Origin header is when used on Object Storages such as AWS S3, Google Storage, etc, where you could save a lot of bandwidth if you configure these services to add a custom header such as:
Access-Control-Allow-Origin: https://mysite.com
This can avoid a common problem called hotlinking that occurs when people use your image on their websites and increases the cost of your Object Store’s bandwidth, even if they didn’t intend to cause any harm.
Access-Control-Allow-Origin can also be used if another site is completely mirroring your own, which negatively affects your site’s SEO. That way none of your website’s assets would be displayed on the mirrored website. However, in these cases you would probably prefer filing a DMCA Takedown Notice to handle the matter faster as the attacker may circumvent the Access-Control-Allow-Origin policy with a proxy.
Access-Control-Allow-Methods
When you’re developing a RESTful API, most of the endpoints will be accepting GET, POST, PUT, PATCH, and DELETE methods. These correspond to action being performed, usually to get, create, update and delete operations.
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
With the help of the Access-Control-Allow-Methods you can specify exactly what kind of HTTP methods your application is supposed to be exposing to external sources, that way you can reserve the use of POST, PUT, PATCH and any type of HTTP method that is used to alter the content of the application to the domain of the application itself and only let the external applications to use GET requests for reading the resources for example.
Access-Control-Allow-Headers
The last of the headers we’ll be mentioning is the Access-Control-Allow-Headers. It’s the most specific of the three and the main reason to use it is to allow custom headers.
An application that uses a “X-My-Header,” for instance, needs to respond to the preflight request with that header into its safelist, for example:
Access-Control-Allow-Headers: X-My-Header
If the header is not allowed, you will see on the Developer Console the error “X header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.”
Preflight Request
A preflight request is an additional HTTP request using the method OPTIONS that the browser will perform for every request that is intended to alter data such as POST, PUT, etc.
It’s a standard behavior for the modern browsers and the expected response from the application is a response with no body but containing the CORS headers with the correct instructions, i.e:
Browser Request OPTIONS /v1/my-api Access-Control-Request-Method: PUT Access-Control-Request-Headers: Origin, X-My-Header Origin: https://sucuri.net Server Response HTTP/1.1 204 No Content Connection: keep-alive Access-Control-Allow-Origin: https://sucuri.net Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE Access-Control-Allow-Headers: X-My-Header
If the preflight fails, the browser will not attempt to send the actual request to the server.
Final Thoughts
CORS is a way to enhance the client-side protection of your application, but it can never be used as the only defense layer. It’s not difficult for an attacker to directly forge a request from any trusted origin, thus you must always implement server-side security policies even if they do exactly what CORS is intended to.
Remember that security is the sum of all details handled properly by professionals. Although the Sucuri Website Firewall doesn’t have a feature that allows you to customize your CORS headers directly from the Firewall dashboard — that has to be done on the application/server; the Firewall will only relay that to the user without interfering.
On the road to a secure website, utilizing a Website Firewall is a must nowadays. You can sign up for a free 30-day trial of the Sucuri Website Firewall.