CORS là gì? Những kiến thức phải biết về Cross-Origin Resource Sharing

Chia sẻ

Khi xây dựng dự án, chắc hẳn không ít lần bạn sẽ gặp phải lỗi CORS – cụ thể lỗi Access to fetch has been blocked by CORS policy xuất hiện trong console của browser.

Qua bài viết này, mình sẽ giúp bạn hiểu sâu hơn về cách hoạt động của CORS từ đó bạn có thể xử lý cách dễ dàng hơn. Trước khi tìm hiểu về CORS, mình sẽ nói qua về Same-Origin Policy để các bạn hiểu.

1. Same-Origin Policy là gì?

Same-Origin Policy là một chính sách bảo mật được thực hiện bởi các trình duyệt để ngăn chặn các tập lệnh truy cập tài nguyên từ các nguồn khác nhau nếu không có sự cho phép. Chính sách để bảo vệ người dùng khỏi các cuộc tấn công bảo mật như: CSRF (Cross-Site Request Forgery) và XSS (Cross-Site Scripting).

Same-Origin được định nghĩa bằng sự kết hợp của ba thành phần:

  • Scheme: Giao thức (HTTP, HTTPS)
  • Host: Tên miền (ví dụ: example.com)
  • Port: Cổng (80, 443,…)

Nếu bất kỳ một trong ba thành phần trên khác biệt, thì được xem là khác nguồn (cross-origin).

Ví dụ:

  • https://example.com và https://example.com:3000 khác nguồn do port khác.
  • https://example.com và http://example.com khác nguồn do Scheme khác
  • https://example.com và https://sub.example.com khác nguồn do Host khác.

2. CORS là gì?

CORS (Cross-Origin Resource Sharing) là một cơ chế bảo mật mà browser sử dụng để ngăn các website từ nguồn khác nhau truy cập vào tài nguyên mà không có sự cho phép.

Ví dụ: Khi một ứng dụng frontend chạy trên https://example.com cần lấy dữ liệu từ server API nằm trên https://api.anotherweb.com, browser sẽ gửi yêu cầu đến server đó. Theo mặc định, browser sẽ chặn request do vi phạm Same-Origin Policy (chính sách cùng nguồn).

Nguồn: 200lab

Bạn muốn cho phép điều này, CORS phải được cấu hình trên server API để xác định website của bạn (example.com) được phép truy cập.

3. Tại sao CORS lại quan trọng?

Trước khi có CORS, browser phải thực hiện rất nhiều biện pháp bảo mật để ngăn chặn các cuộc tấn công từ các website khác nhau. Các cuộc tấn công phổ biến như CSRF (Cross-Site Request Forgery) và XSS (Cross-Site Scripting) có thể lợi dụng việc một website có thể thực hiện yêu cầu đến tài nguyên của một trang khác mà người dùng không hề hay biết.

Same-Origin Policy được phát triển để giải quyết những vấn đề này. Tuy nhiên, có những trường hợp hợp pháp mà bạn thực sự cần truy cập tài nguyên từ một miền khác. Đó là lý do tại sao CORS được ra đời – cho phép bạn xác định các trường hợp ngoại lệ một cách an toàn.

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

Khi một website thực hiện yêu cầu đến server từ nguồn khác, browser sẽ tự động thêm một header Origin vào request, chứa thông tin về nguồn của request.

Server sẽ kiểm tra Origin và quyết định có cho phép hay không bằng cách gửi về các header CORS trong phản hồi, trong đó quan trọng nhất là Access-Control-Allow-Origin. Header này sẽ xác định xem nguồn của request có được phép truy cập tài nguyên hay không.

Có hai loại yêu cầu CORS chính:

  • Simple Request (Các yêu cầu đơn giản): là những request có method là GET, POST, hoặc HEAD, và không có các header, Nếu server cho phép, nó sẽ trả về các header CORS để trình duyệt xử lý.

Ví dụ:

fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => console.log(data));
  • Preflight Request (Yêu cầu tiền kiểm tra): là request OPTIONS được gửi đến máy chủ để kiểm tra xem nó có cho phép yêu cầu từ nguồn khác hay không
  • Khi yêu cầu không thuộc loại “simple”, browser sẽ thực hiện một yêu cầu kiểm tra trước (preflight). Yêu cầu này được gửi bằng phương thức OPTIONS, với mục đích kiểm tra xem yêu cầu chính có được cho phép hay không.
fetch('https://api.example.com/data', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ name: 'John Doe' })
});

5. Client-side CORS và Server-side CORS

CORS có hai khía cạnh quan trọng: Client-side CORS và Server-side CORS, và cả hai đều cần được hiểu rõ để triển khai các yêu cầu cross-origin thành công và bảo mật.

5.1 Client-side CORS

Client-side CORS là cách browser xử lý yêu cầu cross-origin từ ứng dụng frontend của bạn. Khi bạn gửi yêu cầu HTTP từ frontend (thường là qua fetch hoặc XMLHttpRequest), browser sẽ tự động thêm một header Origin vào yêu cầu, xác định nguồn của trang đang gửi yêu cầu.

Trình duyệt sẽ kiểm tra phản hồi từ server để xem liệu server có cho phép yêu cầu cross-origin hay không, dựa vào các header CORS trong phản hồi. Nếu máy chủ không cung cấp các header CORS hợp lệ, trình duyệt sẽ chặn yêu cầu và trả về lỗi trong console.

Nguồn: 200lab

5.2 Server-side CORS

Server-side CORS là cách mà server được cấu hình để xử lý và cho phép các yêu cầu cross-origin. Khi một yêu cầu được gửi đến từ nguồn khác, máy chủ sẽ kiểm tra Origin của yêu cầu đó và quyết định có cho phép hay không bằng cách trả về các header CORS như: Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers.

Nguồn: 200lab

6. Các header CORS quan trọng

CORS dựa vào một số header đặc biệt trong yêu cầu và phản hồi

6.1 Request Headers

  • Origin: Trình duyệt sẽ tự động thêm header này để cho biết trang nào đang gửi yêu cầu. Ví dụ:
Origin: https://example.com

6.2 Response Headers

  • Access-Control-Allow-Origin: đây là header quan trọng nhất. Nó cho biết miền nào được phép truy cập tài nguyên. Ví dụ:
Access-Control-Allow-Origin: https://example.com

Nếu bạn muốn cho phép tất cả các nguồn truy cập, server có thể trả về:

Access-Control-Allow-Origin: *

Nhưng trên thực tế, điều này không được khuyến khích!

  • Access-Control-Allow-Methods: liệt kê các phương thức HTTP mà server cho phép. Ví dụ:
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE
  • Access-Control-Allow-Headers: các header nào có thể được gửi trong yêu cầu. Ví dụ:
Access-Control-Allow-Headers: Content-Type, Authorization
  • Access-Control-Allow-Credentials: cho phép gửi thông tin đăng nhập (cookies, headers Authorization). Giá trị là true nếu server cho phép:
Access-Control-Allow-Credentials: true
  • Access-Control-Max-Age: thời gian mà kết quả của preflight request có thể được cache
Access-Control-Max-Age: 86400

Xem thêm: Local Storage trong JavaScript

7. Các lỗi thường gặp liên quan đến CORS

7.1 Lỗi “No ‘Access-Control-Allow-Origin’ header is present”

Lỗi này xảy ra khi server không trả về header Access-Control-Allow-Origin. Gặp lỗi này, bạn cần kiểm tra, đảm bảo server của bạn được cấu hình thêm header này trong phản hồi.

7.2 Lỗi “CORS policy: Request header field is not allowed by Access-Control-Allow-Headers”

Gặp lỗi này nghĩa là bạn đang gửi header mà server không cho phép, kiểm tra header Access-Control-Allow-Headers từ phản hồi của server và đảm bảo rằng chứa tất cả các header bạn muốn gửi.

8. Cách cấu hình CORS trong Express.js

Trong Express.js, bạn có thể cài đặt middleware để bật CORS:

const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors());

app.get('/data', (req, res) => {
  res.json({ message: 'Hello, world!' });
});

app.listen(8080, () => {
  console.log('Server running on port 8080');
});

9. Kết luận

CORS là một cơ chế bảo mật quan trọng giúp đảm bảo rằng các yêu cầu cross-origin chỉ được thực hiện khi được phép. Mặc dù, việc cấu hình CORS có thể gây nhầm lẫn ban đầu, nhưng hiểu rõ cách hoạt động của nó và cách cấu hình sẽ giúp bạn xây dựng các ứng dụng an toàn và hiệu quả hơn.