1. 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, 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. This way, the destination server where the requested resource resides authorizes 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 receives the cross-origin request, the server determines whether to allow the request based on configured CORS rules. In response, the server returns the Access-Control-Allow-Origin and Access-Control-Allow-Methods headers to indicate whether the request is allowed.
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
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 destination server allows cross-origin requests from the current domain, the browser sends an actual cross-origin request.
The OPTIONS request contains the following headers: Origin, Access-Control-Request-Method, and Access-Control-Request-Headers. After the destination server receives the OPTIONS request, the server specifies the Access-Control-Allow-Origin, Access-Control-Allow-Method, Access-Control-Allow-Headers, and Access-Control-Max-Age headers in the response to indicate whether the request is allowed. If the preflighted request is allowed, the browser sends an actual 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 does not take effect.
3. Implement the CORS mechanism in API Gateway
3.1 Configure the simple request validation mode
By default, all APIs 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
origin: 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 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 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. The custom value of the Access-Control-Allow-Origin header field overrides the default value. The following code snippets show an example in which an API 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
origin: 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 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 to OPTIONS. This way, API Gateway directly passes each OPTIONS request to the backend service of the API. When you create an API that allows only OPTIONS requests, take note of the following items:
When you configure basic information for the API, set Security Authentication to No Authentication.
When you configure request information for the API, enter / in the Request Path field and select the Match All Subpaths check box. After you set HTTP Method to OPTIONS, the Request Mode parameter is automatically set to Pass-through 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 that allows only OPTIONS requests and use this API to configure CORS policies for the API group. You can use curl to test HTTP requests for the API that allows only OPTIONS requests. The following code snippet shows an example of using curl to call the API:
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
origin: 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 that is sent from the backend service of the API 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 that 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
origin: 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 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
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"}