Security Series: Cross-domain resource sharing CORS

flydean發表於2021-09-13

Introduction

What is cross-domain resource sharing? We know that a domain is composed of three parts: scheme, domain, and port. These three parts can uniquely mark a domain or an address requested by a server. Cross-domain resource sharing means that the server allows other domains to access resources in its own domain.

CORS is a mechanism based on HTTP-header detection, which will be explained in detail in this article.

Examples of CORS

For the sake of security, generally a request initiated by a domain can only obtain the domain's own resources, because mutual calls within domain resources are considered safe.

However, with the development of modern browser technology and ajax technology, the demand for requesting other domain resources from javascript has gradually appeared. We call this demand cross-domain request.

For example, the client http://www.flydean.com to the domain http://www.abc.com/data.json.

So how does the client know whether the server supports CORS?

A request called preflight is used here. This request only confirms to the server whether it supports cross-domain requests to access resources. After the client gets a response, it will actually request the cross-domain resources in the server.

Although it is the client that sets the header of the HTTP request to make the CORS request, the server also needs to make some settings to ensure that it can respond to the client's request. So this article is suitable for both front-end developers and back-end developers.

CORS protocol

That's right, if any kind of request is to be standardized, then a standard protocol must be developed. CORS is the same. The CORS protocol mainly defines the request header and response header in HTTP. Let's learn more about them separately.

HTTP request headers

The first is the HTTP request header. The request header is the data that the client carries when requesting resources. The CORS request header mainly contains three parts.

The first part is Origin, which represents the source of the request or preflight request that initiates a cross-domain resource request:

Origin: <origin>

Origin only contains server name information and does not contain any PATH information.

Note that the value of Origin may be null

The second part is Access-Control-Request-Method, which is a preflight request, telling the server the HTTP resource request method that will actually be used next time:

Access-Control-Request-Method: <method>

The third part is Access-Control-Request-Headers, which is also a preflight request, telling the server the header data to be carried in the HTTP request that is actually used next time. The data in the header corresponds to the Access-Control-Allow-Headers on the server side.

Access-Control-Request-Headers: <field-name>[, <field-name>]*

HTTP response headers

With the request from the client, the response from the server is also required. Let's see that the HTTP header data needs to be set on the server.

  1. Access-Control-Allow-Origin

Access-Control-Allow-Origin indicates the CORS domain allowed by the server. You can specify a specific domain, or use * to indicate receiving all domains.

Access-Control-Allow-Origin: <origin> | *
It should be noted that if the request contains authentication information, you cannot use *.

Let's look at an example:

Access-Control-Allow-Origin: http://www.flydean.com
Vary: Origin

The above example indicates that the server is allowed to receive http://www.flydean.com, where a specific domain is specified instead of *. Because the server can set a list of allowed domains, only one of the domain addresses is returned here, so a Vary: Origin header information needs to be added below, indicating that Access-Control-Allow-Origin will request the header with the client The Origin information in the automatically sends changes.

  1. Access-Control-Expose-Headers

Access-Control-Expose-Headers represents the header information that the server allows the client or CORS resources to access at the same time. The format is as follows:

Access-Control-Expose-Headers: <header-name>[, <header-name>]*

E.g:

Access-Control-Expose-Headers: Custom-Header1, Custom-Header2

The above example will expose two headers, Custom-Header1, Custom-Header2, to the client, and the client can get the values of these two headers.

  1. Access-Control-Max-Age

Access-Control-Max-Age indicates how long the result of the preflight request will be cached, the format is as follows:

Access-Control-Max-Age: <delta-seconds>

delta-seconds is in seconds.

  1. Access-Control-Allow-Credentials

This field is used to indicate whether the server accepts the client's request with the credentials field. If used in a preflight request, it indicates whether the subsequent real request supports credentials, and the format is as follows:

Access-Control-Allow-Credentials: true
  1. Access-Control-Allow-Methods

This field indicates the method allowed to access the resource, which is mainly used in the preflight request. The format is as follows:

Access-Control-Allow-Methods: <method>[, <method>]*
  1. Access-Control-Allow-Headers

Used in the preflight request, it means the header field that can be used as a request, and its format is as follows:

Access-Control-Allow-Headers: <header-name>[, <header-name>]*

With the basic concepts of the CORS protocol, we can start using CORS to build cross-domain resource access.

Basic CORS

Let’s take a look at the most basic CORS request. For example, our website is now http://www.flydean.com. In a page of this website, we hope to get https://google.com/data /dataA, then the JS code we can write is as follows:

const xhr = new XMLHttpRequest();
const url = 'https://google.com/data/dataA';

xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();

This request is the most basic CORS request. Let's see what data the request sent by the client contains:

GET /data/dataA HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://www.flydean.com

This request is related to CORS, which means that the source domain of the request is http://www.flydean.com.

The possible return results are as follows:

HTTP/1.1 200 OK
Date: Mon, 01 May 2021 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…Data…]

Note that the above returned result is Access-Control-Allow-Origin: *, which means that the server allows all Origin requests.

Preflighted requests

The above example is the most basic request. The client directly requests resources from the server. Next, let's look at an example of Preflighted requests. The request of Preflighted requests is divided into two parts. The first part is the request judgment, and the second part is the real request.

Note that GET requests will not be sent preflighted.

When will Preflighted requests be sent?

When the client sends the OPTIONS method to the server, for safety reasons, because the server may not be able to accept these OPTIONS methods, the client needs to send one first
Preflighted requests, wait for the server to respond, wait for the server to confirm, and then send the real request. Let us give an example.

const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://google.com/data/dataA');flydean
xhr.setRequestHeader('cust-head', 'www.flydean.com');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<site>www.flydean.com</site>');

In the above example, we sent a POST request to the server, and in this request we added a custom header: cust-head. Because this header is not the standard header in HTTP 1.1, you need to send a Preflighted request first.

OPTIONS /data/dataA HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://www.flydean.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: cust-head, Content-Type

Two extra fields, Access-Control-Request-Method and Access-Control-Request-Headers, are added to the request.

The server response is as follows:

HTTP/1.1 204 No Content
Date: Mon, 01 May 2021 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: http://www.flydean.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: cust-head, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive

The response returned Access-Control-Allow-Origin, Access-Control-Allow-Methods and Access-Control-Allow-Headers.

When the client receives the response from the server and finds that it is equipped with subsequent requests, it can continue to send real requests:

POST /data/dataA HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
cust-head: www.flydean.com
Content-Type: text/xml; charset=UTF-8
Referer: http://www.flydean.com/index.html
Content-Length: 55
Origin: http://www.flydean.com
Pragma: no-cache
Cache-Control: no-cache

<site>www.flydean.com</site>

In the real request, we don't need to send the Access-Control-Request* header mark anymore, just send the real request data.

Finally, we get the response from the server:

HTTP/1.1 200 OK
Date: Mon, 01 May 2021 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: http://www.flydean.com
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some data]

Request with certification

Sometimes, the resources we need to access need to carry authentication information, which is transmitted through HTTP cookies, but for browsers, authentication is not performed by default. To be authenticated, specific flags must be set:

const invocation = new XMLHttpRequest();
const url = 'https://google.com/data/dataA';

function corscall() {
  if (invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}

In the above example, we set the withCredentials flag to indicate that this is a request with authentication.

The corresponding request is as follows:

GET data/dataA HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: http://www.flydean.com/index.html
Origin: http://www.flydean.com
Cookie: name=flydean

We brought cookies in the request, and the response from the server is as follows:

HTTP/1.1 200 OK
Date: Mon, 01 May 2021 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: http://www.flydean.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: name=flydean; expires=Wed, 31-May-2021 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

[text/plain payload]

The server returned Access-Control-Allow-Credentials: true, indicating that the server received credentials authentication, and returned the Set-Cookie option to update the client's cookie.

It should be noted that if the server supports credentials, the returned Access-Control-Allow-Origin, Access-Control-Allow-Headers and Access-Control-Allow-Methods cannot be *.

Summarize

This article briefly introduces the CORS protocol in the HTTP protocol. It should be noted that CORS is actually the interaction between HTTP request headers and response headers.

This article has been included in http://www.flydean.com/cors/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "Program those things", know technology, know you better!

相關文章