1. Cross-origin resource access: Security risks and browser limits

If a resource on a server requests another resource that is deployed in a different domain or uses a different port, the former resource sends a cross-origin HTTP request. For example, an HTML page from the site http://www.aliyun.com sends a request for an image whose URL is http://www.alibaba.com/image.jpg. Most web pages on the Internet support loading resources, such as CSS, images, and scripts, from different domains.

For security reasons, most browsers forbid sending cross-origin requests from web scripts. Other browsers allow you to send cross-origin requests but responses are blocked. This means that when a web application calls an API operation, only the relevant resources in the same domain can be loaded. To load resources from a different domain, you must configure cross-origin resource sharing (CORS) for the API operation. In this way, the destination server where the requested resource resides will authorize the cross-origin request.

The preceding figure shows a typical scenario of cross-origin resource access. By default, mainstream browsers forbid cross-origin resource access for security reasons. However, these browsers support the CORS mechanism that is recommended by the World Wide Web Consortium (W3C). The CORS mechanism is implemented based on a server and a browser. By using this mechanism, you can enable the browser to allow cross-origin requests.

2. CORS overview

2.1 Two request validation modes

The CORS mechanism supports two request validation modes: simple request validation and preflighted request validation.

If a cross-origin request meets all of the following three conditions, the CORS mechanism uses simple request validation to process the cross-origin request.

1. The cross-origin request uses one of the following methods:

  • GET
  • HEAD
  • POST

2. The Content-Type header field in the cross-origin request is set to one of the following values:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

3. The following CORS header fields, including custom header fields in the cross-origin request, are defined in the Fetch standard:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (Note that this header field must be set to one of the values that are listed in the second condition.)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

If a cross-origin request does not meet all of the preceding conditions, the CORS mechanism uses preflighted request validation to process the cross-origin request.

2.2 Simple request validation

In the simple request validation mode, a browser sends a cross-origin request. The Origin header field is specified in the request, indicating that the request is a cross-origin request. After the destination server, where the requested resource resides, receives the cross-origin request, the server determines whether to validate the request based on configured CORS rules. If the validation is successful, the server returns a success response to the browser. The success response includes the Access-Control-Allow-Origin and Access-Control-Allow-Methods header fields. If the validation fails, the server returns an error response to the browser.

As shown in the preceding figure, the success response includes the Access-Control-Allow-Origin header field. To sum up, in the simple request validation mode, a cross-origin request from a browser must include the Origin header field and a success response from the destination server must include the Access-Control-Allow-Origin header field. In this example, the value of the Access-Control-Allow-Origin header field in the success response is *, which indicates that the requested resource can be accessed from all domains. If the destination server allows cross-origin requests only from the site http://www.aliyun.com, the value of this header field must be specified as http://www.aliyun.com, as shown in the following code snippet:

Access-Control-Allow-Origin: http://www.aliyun.com

In this way, cross-origin requests only from the site http://www.aliyun.com are allowed by the destination server.

2.3 Preflighted request validation

In the preflighted request validation mode, after a browser constructs a cross-origin request, the cross-origin request is not immediately sent to the destination server. Instead, a preflighted request is sent to the destination server. The preflighted request is an HTTP OPTIONS request. This request is used to check whether the destination server, where the requested resource resides, allows cross-origin requests from the current domain name. If the response to the preflighted request indicates that the destination server allows cross-origin requests from the current domain name, the browser then sends the cross-origin request to the server.

The OPTIONS request contains the following header fields: Origin, Access-Control-Request-Method, and Access-Control-Request-Headers. After the destination server receives the OPTIONS request, the server determines whether to validate the preflighted request. If the validation is successful, the server specifies the Access-Control-Allow-Origin, Access-Control-Allow-Method, Access-Control-Allow-Headers, and Access-Control-Max-Age header fields in the success response. After the browser receives the success response to the preflighted request, the browser sends the cross-origin request.

The Access-Control-Request-Method header field in the OPTIONS request informs the destination server that the cross-origin request to be sent uses the GET method. The Access-Control-Request-Headers header field in the OPTIONS request informs the destination server that the cross-origin request to be sent contains two custom header fields: X-Ca-Nonce and Content-Type. Based on these two header fields in the OPTIONS request, the destination server determines whether to allow the cross-origin request.

The Access-Control-Allow-Methods header field in the success response to the OPTIONS request indicates that the destination server allows a cross-origin request from the browser to use the GET method. If multiple methods are allowed, the methods are separated with commas (,).

The Access-Control-Allow-Headers header field in the success response to the OPTIONS request indicates that the destination server allows a cross-origin request to contain the X-Ca-Nonce and Content-Type header fields. The header fields are separated with commas (,).

The Access-Control-Max-Age header field in the success response to the OPTIONS request indicates that the response is valid for 86,400 seconds, namely, 24 hours. Within this validity period, if the browser needs to send the same cross-origin request again, the browser does not need to send another preflighted request. Note that the browser itself specifies a validity period for the cross-origin request. If the value of the Access-Control-Max-Age header field exceeds the validity period that is specified by the browser, the Access-Control-Max-Age header field will not take effect.

3. Implement the CORS mechanism in API Gateway

3.1 Configure the simple request validation mode

By default, all API operations in API Gateway support cross-origin calls. Therefore, by default, API Gateway adds the Access-Control-Allow-Origin header field and specifies its value as * in each API response. The following code snippets show an example of this process:

An API request from a client

GET /simple HTTP/1.1
Host: www.alibaba.com
orgin: http://www.aliyun.com
content-type: application/x-www-form-urlencoded; charset=utf-8
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT		

The response that is sent from the backend service of the API operation to API Gateway

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12

{"200","OK"}		

The response that is sent from API Gateway to the client

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2017 09:53:23 GMT
Access-Control-Allow-Origin: *
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Content-Type: application/json; charset=UTF-8
Content-Length: 12

{"200","OK"}	

As shown in the preceding code snippets, API Gateway adds specific information to the response that is sent from the backend service, including the following information:

Access-Control-Allow-Origin: *	

API Gateway specifies the Access-Control-Allow-Origin header field as *, which indicates that the API operation can be called from all domains.

If you need to specify the Access-Control-Allow-Origin header field as another value, add the Access-Control-Allow-Origin header field as a response header field when you configure response information for the API operation. The custom value of the Access-Control-Allow-Origin header field will override the default value. The following code snippets show an example in which an API operation can be called only from the site http://www.aliyun.com:

An API request from a client

GET /simple HTTP/1.1
Host: www.alibaba.com
orgin: http://www.aliyun.com
content-type: application/x-www-form-urlencoded; charset=utf-8
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT	

The response that is sent from the backend service of the API operation to API Gateway

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12

{"200","OK"}

The response that is sent from API Gateway to the client

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12

{"200","OK"}

3.2 Configure the preflighted request validation mode

API Gateway allows you to set the HTTP request method of an API operation to OPTIONS. In this case, API Gateway will directly pass each OPTIONS request to the backend service of the API operation. When you create an API operation that allows only OPTIONS requests, take note of the following items:

  • When you configure basic information for the API operation, set Security Certification to No Certification.
  • When you configure request information for the API operation, enter / in the Request Path field and select the Match All Child Paths check box. After you set HTTP Method to OPTIONS, the Request Mode parameter is automatically set to Request Parameter Passthrough and cannot be modified. You do not need to define request parameters for the API operation.

For each API group, you can create an API operation that allows only OPTIONS requests and use this API operation to configure CORS policies for the API group. You can use curl to test HTTP requests for the API operation that allows only OPTIONS requests. The following code snippet shows an example of using curl to call the API operation:

sudo curl -X OPTIONS -H "Access-Control-Request-Method:POST" -H "Access-Control-Request-Headers:X-CUSTOM-HEADER" http://ec12ac094e734544be02c928366b7b26-cn-qingdao.alicloudapi.com/optinstest -i

HTTP/1.1 200 OK
Server: Tengine
Date: Sun, 02 Sep 2018 15:32:19 GMT
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-CUSTOM-HEADER
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 1016AC86-E345-405C-8049-A6C24078F65F
			

When you configure the API operation that allows only OPTIONS requests, note that API Gateway will add four header fields to each response from the backend service of the API operation: Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, and Access-Control-Max-Age. Therefore, you must add the four header fields as response header fields for the API operation and specify their values as needed, so that the custom values will override the default values.

The following code snippets show an example of a cross-origin API call in the preflighted request validation mode:

An OPTIONS request from a client

OPTIONS /simple HTTP/1.1
Host: www.alibaba.com
orgin: http://www.aliyun.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT

The response to the OPTIONS request, which is sent from the backend service of the API operation to API Gateway

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Headers: X-CUSTOM-HEADER
Access-Control-Max-Age: 10000
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8

The response to the OPTIONS request, which is sent from API Gateway to the client

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Headers: X-CUSTOM-HEADER
Access-Control-Max-Age: 10000
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8

A cross-origin API request from the client

GET /simple HTTP/1.1
Host: www.alibaba.com
orgin: http://www.aliyun.com
content-type: application/x-www-form-urlencoded; charset=utf-8
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT

The response to the cross-origin request, which is sent from the backend service of the API operation to API Gateway

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12

{"200","OK"}

The response to the cross-origin request, which is sent from API Gateway to the client

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12

{"200","OK"}