CORS

Có bao giờ bạn gặp lỗi này chưa???

Screen Shot 2019-06-08 at 23.11.51.png

Nếu bạn lập trình web, rất có thể là bạn đã gặp trường hợp này rồi. Ở đây có thể thấy là resource mà chúng ta cần truy cập đã bị block bởi CORS policy, và nó phàn nàn rằng:

No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Không có header gọi là ‘Access-Control-Allow-Origin’ ở trong các resource được request tới???

Bạn không hiểu chuyện gì xảy ra :-?, nhưng rõ ràng nó ngăn bạn có được dữ liệu bạn cần để hiển thị lên trang web của mình.

Hôm nay mình sẽ giải thích giùm bạn và tiện thể gợi ý một số cách để vượt qua tình huống này. Read on!

Đầu tiên CORS policy là gì?

Cross-Origin Resource Sharing (CORS policy), tạm dịch là Cơ chế chia sẻ tài nguyên giữa các origin. Tiện thể Origin giống như địa chỉ của trang web, bao gồm protocol + domain + port, chẳng hạn https://example.com/my-site thì  protocol là https, domain là example.com, và port là 80 (default 80 nếu không ghi cụ thể). CORS là cơ chế giúp một một trang web từ một origin này có thể access đến resource từ một origin khác. Đó là vì các browser hiện tại đều có cơ chế bảo mật gọi là Same-Origin policy, trong đó giới hạn một trang web chỉ có thể truy xuất đến các tài nguyên trong cùng origin của nó.

Tại sao lại có Same-Origin policy?

Các browser dùng cookies để lưu các thông tin bảo mật của người dùng, chẳng hạn user/password của một ngân hàng https://newbank.com. Mỗi request của người dùng đến domain này sau khi đã đăng nhập đều được đính kèm cookies đó giúp cho việc xác thực người dùng. Vấn đề xảy ra khi người dùng truy cập tiếp vào một trang mới http://malicious-site.com, trang web này có thể thực hiện các AJAX call đến trang https://newbank.com giống như một người đã được authenticate bình thường. Lý do là browser tự động attach cookies của một domain mỗi khi có request tới domain đó (cho dù request được xuất phát từ một domain khác). Vậy là kẻ tấn công đã có thể thực hiện các hành vi như rút tiền từ tài khoản ngân hàng của bạn mà không cần biết thông tin đăng nhập @@!

Bằng cách check xem request có origin giống origin đích không, browser ngăn chặn việc request từ những trang web khác có thể gây nguy hại cho bạn. Tuy vậy điều này cũng gây ra phiền toái là bạn không thể request đến các trang web khác để lấy dữ liệu, đây là lí do mà CORS ra đời.

CORS hoạt động như thế nào?

Hãy xem lại lỗi chúng ta đã thấy ở đầu bài viết. Chính CORS policy chứ không phải Same-Origin policy đã ngăn chặn việc truy cập. Policy này yêu cầu một cái gọi là  ‘Access-Control-Allow-Origin’ header ở response của resource chúng ta request tới.

Có 2 loại CORS request:

  1. Simple requests
  2. Preflighted requests

Simple requests

Là các request mà không yêu cầu có preflight request (request để check sơ bộ, sẽ giải thích sau). Chẳng hạn khi bạn truy cập vào trang https://mybank.com và trang web này gọi tới https://api.mybank.com để lấy dữ liệu. Khi đó AJAX request được tạo ra là:

GET /account-info/ HTTP/1.1
Host: api.mybank.com
Origin: https://www.mybank.com
[Rest of request...]

Server nhận được request này sẽ check xem origin trong request header có được phép không, nếu có nó sẽ gán thêm một giá trị gọi là Access-Control-Allow-Origin vào header của response trả về

HTTP/1.1 200 OK  
Access-Control-Allow-Origin: https://www.mybank.com  
Content-Type: application/json
[Rest of response...]  

Khi nhận được response, browser sẽ check xem Access-Control-Allow-Origin trong header có match với origin của tab đang mở không. Nó sẽ match khi mà giá trị này trùng với origin của tab đang mở hoặc bằng “*” (cho phép tất cả các request được truy cập).

Screen Shot 2019-06-09 at 11.53.22

Trường hợp không có Access-Control-Allow-Origin thì bạn sẽ nhận được lỗi như đã đề cập

Screen Shot 2019-06-08 at 23.11.51.png

Preflighted Requests

Là các request mà yêu cầu có trước một preflight request (request check sơ bộ hay thăm dò), thực ra là các request OPTIONS tới cùng URL của request đó. Các trường hợp yêu cầu có request này là:

  1. Khi một website dùng phương thức POST để đẩy JSON data tới một REST API, tức là Content-Type header của request là application/json.
  2. Khi trang web gọi tới một API trong đó có kèm một token để authenticate trong request header chẳng hạn Authorization

Như vậy các prefighted requests thường rơi vào trường hợp gọi tới các REST API.

Ví dụ về preflighted request

Đầu tiên browser gửi một request OPTIONS

OPTIONS /withdraw/ HTTP/1.1
Host: api.mybank.com
Origin: https://www.mybank.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
[Rest of request...]

Sau đó server phản hồi lại, trường hợp này là server check OK

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mybank.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Content-Type: application/json
[Rest of response...]  

Bây giờ browser mới chính thức gửi request POST tới server

POST /withdraw/ HTTP/1.1
Host: api.mybank.com
Authorization: 1234567
Content-Type: application/json
Origin: https://www.mybank.com
[Rest of request...]

Khi này request đã được server check thành công

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mybank.com
Content-Type: application/json
[Rest of response...]  

Làm sao để khắc phục lỗi CORS blocked?

Như đã mô tả ở trên, CORS policy giúp cho việc bảo mật trang web, chống lại các hành vi bất hợp pháp. Khi gặp trường hợp lỗi CORS có nghĩa là bạn đã truy cập từ một trang web không được phép.

  1. Update server

Để có thể truy cập vào api.mydomain.com từ một trang web nào đó chẳng hạn mydomain.com hay mywebsite.com thì bắt buộc server web của trang api.mydomain.com phải cho phép các trang web đó truy cập vào trong list Access-Control-Allow-Origin hoặc cho phép tất cả các request đều được truy cập (gán giá trị là “*”)

Ví dụ dưới đây là cấu hình cho server NodeJS

var express = require('express');
var cors = require('cors');
var app = express();
app.use(cors());
(...)

2. Dùng JSONP

JSONP là một cách để có thể request tới một URL từ một địa chỉ không match với list Access-Control-Allow-Origin. JSONP kèm một script để chạy lúc nhận được response từ server. Do vậy response phải có dạng

callbackName([ { name: ‘some value' }, { name: ‘some other value' }])

Request URL

http://mydomain/resource/resourceid?callback=callbackName

Hạn chế của phương pháp này là chỉ hỗ trợ method GET.

3. Dùng Proxy server

Ta cũng có thể xử lý ở phía server bằng thêm 1 server proxy

"proxies": [
    {
        "path": "/yelp/v3/businesses",
        "proxyUrl": "https://api.yelp.com/v3/businesses"
    }
]

 

Leave a comment