Clarifying CORS - Cross-origin Resource Sharing
Intro
Continuing on from my previous blog about website security week, we're going to talk about CORS on the web.
CORS is Cross-origin Resource Sharing this is often used when your website is hosted separately from your API. e.g. your website is at website.com and calls your API at api.com. This is a common architectural pattern as it allows each API and website to move independently and faster, however it can introduce some security issues.
Access-Control Headers
To allow CORS requests your API will need to response with certain headers, which allow certain behaviors from your website/frontend.
Access-Control-Allow-Origin
This header can be set with either the origin
which will be calling the API, it can only be a single origin. Otherwise it can be a *
however this doesn't allow credentials to be passed, which we will talk about later.
If at all possible prefer setting a specific origin to a *
.
Example Usage:
Access-Control-Allow-Origin: https://mozilla.org
Access-Control-Allow-Methods
This header can be set with a list of HTTP Methods that are allowed to be used to contact your API.
Generally speaking OPTIONS will want to be part of this list as any frontend will make a OPTIONS request, often referred to as a preflight request, before it makes the actual request. OPTIONS requests won't be made for GET requests.
It can also be *
however, you should be specific if you can.
Example Usage:
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age
This header can be set to a time period that the frontend will cache the preflight OPTIONS request. It is a value in seconds for example 86400 seconds is 24 hours.
Let's say you've set Max-Age to the above, this means that the first request you make from the frontend to the API will make an OPTIONS request and then the actual request. It will subsequently won't have to make another OPTIONS request to that API for 24 hours.
Example Usage:
Access-Control-Max-Age: 86400
Access-Control-Allow-Headers
This header can be set with a list of Headers that are allowed to be passed onto your API.
Example Usage:
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Allow-Credentials
This header specifies if to include credentials in the request. Credentials count as cookies, authorization headers or TLS client certificates.
Example Usage:
Access-Control-Allow-Credentials: true
Rate Limiting
You should note that Access-Control-Allow-Origin
header only prevents browsers from making requests to the API. It does not prevent calls to your API from other machines, command line, Postman etc. You should ensure that you have put other security measures in place to prevent misuse of your API, including Authentication and Rate Limiting.
Rate Limiting involves restricting too many calls being made to your API. It can be done in a number of ways depending on how your API is developed. I would look for libraries to help manage this for you.
Summary
In summary, separating your API and website can produce real development benefits however it can introduce security problems and having to deal with CORS. Hopefully this helps clarify CORS and how you can secure it.
Set those headers now!
Happy Building!