DEV Community

Julio Castillo Anselmi
Julio Castillo Anselmi

Posted on

All CORS explained from A to Z

CORS definition

Before defining CORS, some fundamental concepts to understand CORS will be defined.

origin

Given a URL, the origin is the first part that is made up of the scheme: the host and the port (if present).

source = scheme + host + port

For example, in https://localhost:8080/surveys the source is https://localhost:8080 and /survey is the path

same-origin

It refers to the case where the client makes a request to a server and the server has the same origin as the client.

https://aurora.net/home -> https://aurora.net/api/pos?id=290
Enter fullscreen mode Exit fullscreen mode

cross-origin (cross-origin)

The client makes a request to a server whose origin is different from that of the client.

https://localhost:8080/home -> https://aurora.net/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:8080/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:3000/api/pos?id=290
Enter fullscreen mode Exit fullscreen mode

Same-origin policy and CORS

By default and by security policy, browsers only allow HTTP requests to the same origin, that is, to the same domain as the page that makes the request.

CORS (Cross-Origin Resource Sharing) is the mechanism by which browsers allow a request to be made from one origin to a different one, cross-origin.

In order to make a CORS request, the client does not have to do anything, it simply launches the request. The browser makes the request and it is the server that must respond if it accepts requests from that origin. It does this by sending the Access-Control-Allow-Origin header response. This header can have an asterisk (*) or a specific origin. The * means that the server accepts requests from any origin.
The browser reviews the browser's response. If you have the Access-Control-Allow-Origin header and it has the value * or the origin matches the client origin, then the browser accepts the response. Otherwise, it discards it and generally displays a CORS-descriptive error on the console.

 1. browser with origin X makes a request to origin Y
 2. server responds
 3. If the response from the server includes the header `Access-Control-Allow-Origin` Then
 4. If (Access-Control-Allow-Origin == '*') or (Access-Control-Allow-Origin == X) Then
 5. browser accepts the response
 6. If not
 7. discard response and show error
 8. End Yes
 9. If not
10. discard response and show error
11. End Yes
Enter fullscreen mode Exit fullscreen mode

Access-Control-Allow-Origin can also be set to null for unknown origins (such as accessing from a file instead of a website). This is useful while you are in a development or testing phase, this way you don't need to put '*' which is less restrictive. Access-Control-Allow-Origin: null

Preflight

Above we have seen in a generic way how a cross-origin request should be treated using CORS. Actually, before making a cross-origin request, browsers that implement CORS will "meta" the actual request.

A preflight is a request that the browser makes before making a CORS request if the request meets one of these criteria:

  • Use an HTTP method other than GET, POST, or HEAD
  • Set the Content-Type header to a value other than: -application/x-www-form-urlencoded
    • multipart/form-data -text/plain
  • Set additional headers other than: -Accept
    • Accept-Language
    • Content-Language
  • The XMLHttpRequest object contains upload events

The HTTP methods GET, POST and HEAD are called simple methods. Simple methods do not need to be listed in the Control-Allow-Methods header.

The preflight is a request with the OPTIONS HTTP method. This "meta" request that the browser makes is for the server to decide how to respond to CORS. In the preflight the browser sends the headers:

  • Origin
  • Access-Control-Request-Method (supports only one method)
  • Access-Control-Request-Headers (only if the headers are not simple. You can have a comma-separated list of headers)

The server checks if the HTTP origin and method are in its list of allowed origins and methods (it will also check the headers indicated in Access-Control-Request-Headers, if any). If this check is favorable, it responds with a status code in the range 200. The answer should not wear a bodysuit. The headers sent by the server are:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods (in this case it can be a comma separated list of commands)
  • Access-Control-Allow-Headers

Example of preflight request:

OPTIONS /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Security-Level, App-Name
Enter fullscreen mode Exit fullscreen mode

Example preflight response:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http//localhost:3000
Access-Control-Allow-Methods: GET, DELETE
Access-Control-Allow-Headers: Security-Level, App-Name
Enter fullscreen mode Exit fullscreen mode

Request example:

GET /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Security-Level: High
App-Name: Survey
Enter fullscreen mode Exit fullscreen mode

The previous algorithm, now modified with the preflights, would look like this:

  1. the Javascript client whose origin is X, makes a request P to the server with origin Y.
        P contains the HTTP method M and a set of headers C
  2. If M is a simple HTTP method and all C headers are simple Then
  3. send the request P to the server
  4. If not
  5. prepare a preflight request F with HTTP OPTIONS method and a set of headers C' with:
         Origin = X, Access-Control-Request-Method = M
  6. If in C there are headers that are not simple Then
  7. add to C' the Access-Control-Request-Headers header with the list of non-simple C headers
  8. End Yes
  9. send preflight F to server
10 If server responds to preflight with status in the range of 200 Then
11. send the request P to the server
12. If not
13. Check the Access-Control-Allow-Origin response headers,
           Access-Control-Allow-Methods and Access-Control-Allow-Headers to determine which
           are the validations that have failed and report the error
14. End Yes
15. End Yes
Enter fullscreen mode Exit fullscreen mode

Cache. The browser can cache the preflights and thus avoid making an extra request for each client request that would require a preflight. The cache saves on its origin + path entries.

Canvas and images

For images whose src attribute is a different origin than the client, the browser loads the resource and displays it but does not allow manipulations with it such as toBlob, toDataURL, and getImageData.
To enable CORS in the case of images, there is the crossOrigin attribute that can take the values anonymous or user-credentials. user-credentials is like withCredentials in XMLHttpRequest, ie user credentials (eg cookies) are sent with the request. If it is not necessary to send the credentials, it is preferable to send anonymous.

Cookies and user credentials

By default the browser does not send user credentials as cookies in cross-origin requests. The browser also does not expose all the response headers that the server sends.
That's why CORS has mechanisms to remedy those restrictions: the Access-Control-Allow-Credentials and Access-Control-Expose-Headers response headers.

So for the use of cookies it is necessary for the client to indicate:

xhr.withCredentials = true;
document.cookie = someUniqueId;
Enter fullscreen mode Exit fullscreen mode

The server must respond to the preflight and the request with the headers
Access-Control-Allow-Credentials and Access-Control-Allow-Origin.

When the server sends the Access-Control-Allow-Credentials header with the value true, it must send the Access-Control-Allow-Origin header with a specific origin; Access-Control-Allow-Origin: * is invalid because accepting cookies from any origin can cause security problems.

Valid:

Access-Control-Allow-Origin: http://localhost:1111
Access-Control-Allow-Credentials: true
Enter fullscreen mode Exit fullscreen mode

Invalid:

Access-Control-Allow-Origin: \*
Access-Control-Allow-Credentials: true
Enter fullscreen mode Exit fullscreen mode

In the case where the client has xhr.withCredentials = true and the server responds with Access-Control-Allow-Origin: true, the server can set cookies on the client. These cookies will be sent by the browser in successive requests, however, the client will not be able to read these cookies with Javascript.

Response headers

The XMLHttpRequest object exposes two methods for reading response headers: getResponseHeader and getAllResponseHeaders. In a same-origin request these methods return all the headers sent by the server. When dealing with a cross-origin request, only the plain headers can be read.

Using the Access-Control-Expose-Headers header the server can specify which headers the client can read.

Access-Control-Expose-Headers: X-Powered-By
Enter fullscreen mode Exit fullscreen mode

Simple headers:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Summary of headers used in CORS

Request headers (sent by client browser)

  • Origin: Header added by the browser. Contains the origin of the client in the request. Counterpart: Access-Control-Allow-Origin.
  • Access-Control-Request-Method: Header added by the browser in the preflight. Contains the HTTP method that the client will do on the actual request. Counterpart: Access-Control-Allow-Methods.
  • Access-Control-Request-Headers: Header added by the browser in the preflight. Contains the HTTP headers that the client will send in the actual request. Counterpart: Access-Control-Allow-Headers.

Response headers (sent by the server)

  • Access-Control-Allow-Origin: The value * indicates that all origins are allowed
    A specific value indicates that the server only accepts requests from that source
    The value null indicates that it only accepts requests from sources that are not a web site, for example a file (only recommended for development and testing). Counterpart: Origin

  • Access-Control-Allow-Credentials: The value true indicates that the server supports the use of user credentials such as cookies.

  • Access-Control-Allow-Methods: Has a list of HTTP methods accepted by the server.
    It is not necessary to include the simple methods, although it is good practice to do so.
    This header should only be sent in response to preflight requests. Counterpart: Access-Control-Request-Methods

  • Access-Control-Allow-Headers: Has a list of HTTP headers accepted by the server.
    This header should only be sent in response to preflight requests. Counterpart: Access-Control-Request-Headers

  • Access-Control-Max-Age: Its value specifies the maximum time in seconds that it is recommended to keep a preflight cache, although the browser can have its own maximum value.
    This header should only be sent in response to preflight requests.

  • Access-Control-Expose-Headers: Contains the headers that can be read by the browser. You can have a comma separated list of headers.
    It is optional and is not required for the CORS request to be successful.

Top comments (0)