CORS là gì? Cách hoạt động và xử lý lỗi CORS thường gặp

Ngày đăng:

Cập nhật mới:

06/11/2026

Tìm hiểu CORS là gì, cách hoạt động của Simple Request, Preflight Request, HTTP Header và hướng dẫn cấu hình, khắc phục lỗi CORS hiệu quả.

Khi phát triển ứng dụng web, bạn có thể gặp tình trạng API hoạt động bình thường trên Postman nhưng lại báo lỗi khi gọi từ trình duyệt. Một trong những nguyên nhân phổ biến là CORS (Cross-Origin Resource Sharing). Đây là cơ chế cho phép máy chủ kiểm soát những website được phép đọc tài nguyên khác origin thông qua HTTP Header. Hiểu rõ CORS giúp Developer xử lý lỗi kết nối, cấu hình API phù hợp và triển khai các ứng dụng sử dụng dịch vụ lưu trữ như S3 Object Storage hiệu quả hơn.

CORS là gì?

CORS (Cross-Origin Resource Sharing) là cơ chế chia sẻ tài nguyên khác nguồn, cho phép máy chủ xác định những origin nào được trình duyệt chấp nhận khi truy cập và đọc tài nguyên thông qua HTTP Header.

CORS hoạt động dựa trên sự phối hợp giữa trình duyệt và máy chủ. Khi một trang web gửi request đến tài nguyên thuộc origin khác, trình duyệt sẽ kiểm tra các HTTP Header trong phản hồi để xác định JavaScript có được phép đọc dữ liệu hay không.

Một số đặc điểm quan trọng của CORS:

  • Không phải phần mềm: CORS không phải ứng dụng, giao thức truyền dữ liệu hay một hệ thống độc lập mà là cơ chế được triển khai dựa trên HTTP Header.
  • Hoạt động thông qua trình duyệt: CORS chủ yếu kiểm soát quyền truy cập tài nguyên khác origin của các ứng dụng web chạy trên trình duyệt.
  • Máy chủ quyết định quyền chia sẻ: Server sử dụng các header như Access-Control-Allow-Origin để khai báo những origin được phép.
  • Không thay thế xác thực: CORS không xác minh danh tính người dùng, không cấp quyền truy cập API và không thay thế các cơ chế phân quyền.

Ví dụ, một website có địa chỉ https://frontend.example.com muốn lấy dữ liệu từ API https://api.example.com. Vì hai địa chỉ có hostname khác nhau nên đây là request cross-origin.

Nếu API cho phép origin https://frontend.example.com thông qua cấu hình CORS phù hợp, trình duyệt có thể cho phép JavaScript đọc dữ liệu trả về từ API.

khai-niem-cors
Khái niệm về Cors

Same-Origin Policy và vì sao cần CORS?

Để hiểu CORS, trước tiên cần biết Same-Origin Policy (SOP). Đây là chính sách bảo mật được trình duyệt áp dụng nhằm hạn chế các trang web truy cập dữ liệu từ origin khác.

1. Origin gồm những thành phần nào?

Origin là định danh của một nguồn web, được xác định bởi ba thành phần:

  • Scheme: Giao thức sử dụng, chẳng hạn HTTP hoặc HTTPS.
  • Host: Tên miền hoặc địa chỉ máy chủ.
  • Port: Cổng kết nối, chẳng hạn 80, 443 hoặc 8080.

Hai URL chỉ được xem là cùng origin khi cả ba thành phần trên đều giống nhau.

Ví dụ, giả sử website đang hoạt động tại https://example.com:

URL được truy cậpCùng origin?Nguyên nhân
https://example.com/apiCóCùng scheme, host và port
http://example.com/apiKhôngKhác scheme
https://api.example.comKhôngKhác host
https://example.com:8443KhôngKhác port

Lưu ý, đường dẫn URL (path), query string và fragment không thuộc thành phần xác định origin. Vì vậy, https://example.com/api và https://example.com/users vẫn cùng origin.

2. Vì sao cần CORS bên cạnh Same-Origin Policy?

Same-Origin Policy giúp hạn chế việc một website độc hại đọc dữ liệu từ website khác mà người dùng đang đăng nhập. Tuy nhiên, trong thực tế, các ứng dụng web thường được triển khai trên nhiều domain hoặc subdomain khác nhau.

Ví dụ:

  • Frontend: https://app.example.com
  • Backend API: https://api.example.com
  • Dịch vụ lưu trữ: https://storage.example.com

Mặc dù cùng thuộc một hệ thống, các địa chỉ này vẫn khác origin theo quy tắc của trình duyệt.

Nếu chỉ áp dụng Same-Origin Policy mà không có cơ chế cho phép ngoại lệ, ứng dụng frontend sẽ không thể đọc phản hồi từ API khác origin thông qua JavaScript.

CORS giải quyết vấn đề này bằng cách cho phép server khai báo những origin được quyền đọc tài nguyên.

Có thể hiểu đơn giản:

  • Same-Origin Policy đặt ra nguyên tắc hạn chế truy cập khác nguồn.
  • CORS cho phép server khai báo những trường hợp ngoại lệ được chấp nhận.
  • Trình duyệt kiểm tra các header CORS để quyết định có cho JavaScript truy cập phản hồi hay không.

Nhờ đó, Developer có thể xây dựng ứng dụng frontend và backend trên các domain riêng mà vẫn duy trì cơ chế kiểm soát truy cập của trình duyệt.

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

CORS hoạt động thông qua việc trình duyệt gửi HTTP Request và kiểm tra các header trong HTTP Response từ server. Tùy vào phương thức, request header và thông tin xác thực, trình duyệt có thể xử lý request theo dạng Simple Request hoặc Preflight Request.

1. Simple Request

Simple Request là request cross-origin đáp ứng các điều kiện nhất định của Fetch Standard và không cần thực hiện bước kiểm tra Preflight trước khi gửi request chính.

Một request được xem là Simple Request khi đáp ứng các điều kiện cơ bản:

  • Sử dụng một trong ba phương thức GET, HEAD hoặc POST.
  • Các request header thuộc nhóm CORS-safelisted request-header và đáp ứng điều kiện về giá trị.
  • Nếu sử dụng Content-Type, giá trị phải thuộc một trong ba loại được cho phép: application/x-www-form-urlencoded, multipart/form-data hoặc text/plain.

Ví dụ, frontend gửi request GET đến API:

fetch("https://api.example.com/users", {
  method: "GET"
});

Trình duyệt sẽ tự động thêm header Origin vào request:

GET /users HTTP/1.1
Host: api.example.com
Origin: https://frontend.example.com

Server phản hồi:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.example.com
Content-Type: application/json

Sau khi nhận phản hồi, trình duyệt kiểm tra Access-Control-Allow-Origin.

Nếu giá trị này khớp với origin của trang web hoặc được thiết lập thành * trong trường hợp phù hợp, trình duyệt cho phép JavaScript đọc dữ liệu trả về.

Nếu server không cung cấp header hợp lệ, trình duyệt sẽ chặn quyền truy cập response của JavaScript. Tuy nhiên, request GET ban đầu vẫn có thể đã được gửi và xử lý tại server.

2. Preflight Request

Preflight Request là request kiểm tra trước được trình duyệt tự động gửi đến server bằng phương thức OPTIONS để xác định request thực tế có được phép thực hiện hay không.

Cơ chế này thường xuất hiện khi request sử dụng phương thức như PUT, DELETE hoặc có các header không thuộc nhóm CORS-safelisted.

Ví dụ, frontend muốn cập nhật thông tin người dùng thông qua API:

fetch("https://api.example.com/users/1", {
  method: "PUT",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer TOKEN"
  },
  body: JSON.stringify({
    name: "Nguyen Van A"
  })
});

Trước khi gửi request PUT, trình duyệt sẽ gửi một request OPTIONS:

OPTIONS /users/1 HTTP/1.1
Host: api.example.com
Origin: https://frontend.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type

Server cần phản hồi các header cho phép tương ứng:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 3600

Nếu phản hồi đáp ứng các yêu cầu CORS, trình duyệt mới tiếp tục gửi request PUT thực tế.

Quy trình Preflight có thể hiểu qua ba bước:

  1. Trình duyệt gửi OPTIONS để thông báo origin, phương thức và các header dự kiến sử dụng.
  2. Server kiểm tra và phản hồi những phương thức, header được phép.
  3. Nếu kết quả kiểm tra hợp lệ, trình duyệt gửi request chính đến server.

Kết quả Preflight có thể được trình duyệt lưu vào bộ nhớ đệm theo thời gian được khai báo trong Access-Control-Max-Age, giúp hạn chế việc gửi OPTIONS lặp lại.

quy-trinh-preflight-co-the-hieu-qua-ba-buoc
Quy trình Preflight

3. Request có kèm Credentials

Credentials là thông tin xác thực được gửi kèm request, chẳng hạn cookie, thông tin xác thực HTTP hoặc chứng chỉ phía máy khách trong các trường hợp tương ứng.

Khi frontend cần gửi cookie đến API khác origin, Developer phải cấu hình cả phía client và server.

Ví dụ phía frontend:

fetch("https://api.example.com/profile", {
  method: "GET",
  credentials: "include"
});

Phía server cần phản hồi:

Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Credentials: true

Có hai nguyên tắc quan trọng:

  • Khi cho phép request có credentials, Access-Control-Allow-Origin không được sử dụng giá trị wildcard * mà phải chỉ định origin cụ thể.
  • Access-Control-Allow-Credentials phải có giá trị true để trình duyệt cho phép chia sẻ phản hồi của request có credentials.

Ngoài ra, nếu sử dụng cookie cross-site, cần kiểm tra thêm các thuộc tính SameSite, Secure và chính sách cookie của trình duyệt. Cấu hình CORS đúng không đồng nghĩa cookie chắc chắn được gửi thành công.

Một điểm cần phân biệt là header Authorization không tự động yêu cầu Access-Control-Allow-Credentials: true. Header này cần được cho phép thông qua Access-Control-Allow-Headers nếu request sử dụng nó.

Các HTTP Header CORS quan trọng

CORS sử dụng nhiều HTTP Header để trình duyệt và server trao đổi thông tin về quyền truy cập tài nguyên khác origin. Có thể chia các header này thành hai nhóm chính: Request Header và Response Header.

HeaderNhómChức năng
OriginRequestXác định origin gửi request
Access-Control-Request-MethodRequestKhai báo phương thức dự kiến sử dụng trong request chính
Access-Control-Request-HeadersRequestKhai báo các header dự kiến gửi trong request chính
Access-Control-Allow-OriginResponseXác định origin được phép truy cập
Access-Control-Allow-MethodsResponseKhai báo các phương thức HTTP được phép
Access-Control-Allow-HeadersResponseKhai báo các request header được phép
Access-Control-Allow-CredentialsResponseCho phép chia sẻ phản hồi của request có credentials
Access-Control-Expose-HeadersResponseCho phép JavaScript đọc thêm các response header
Access-Control-Max-AgeResponseXác định thời gian lưu kết quả Preflight

Trong đó, một số header có vai trò đặc biệt quan trọng:

  • Access-Control-Allow-Origin: Đây là header xác định origin được phép đọc tài nguyên. Server có thể khai báo một origin cụ thể hoặc sử dụng * trong những trường hợp không yêu cầu credentials.
  • Access-Control-Allow-Methods: Header này cho biết những phương thức HTTP được phép thực hiện thông qua CORS, chẳng hạn GET, POST, PUT hoặc DELETE.
  • Access-Control-Allow-Headers: Cho phép server khai báo các request header mà trình duyệt có thể sử dụng trong request cross-origin, ví dụ Authorization và Content-Type.
  • Access-Control-Allow-Credentials: Cho biết server có cho phép chia sẻ phản hồi của request sử dụng credentials hay không.
  • Access-Control-Expose-Headers: Mặc định, JavaScript chỉ có thể đọc một số response header thuộc nhóm CORS-safelisted. Nếu muốn đọc thêm header như ETag, server cần khai báo header này trong Access-Control-Expose-Headers.
  • Access-Control-Max-Age: Xác định thời gian trình duyệt có thể lưu kết quả kiểm tra Preflight. Thời gian thực tế còn phụ thuộc giới hạn bộ nhớ đệm của từng trình duyệt.

5. Lỗi CORS là gì? Nguyên nhân và cách nhận biết

Lỗi CORS xảy ra khi trình duyệt không thể xác nhận quyền truy cập tài nguyên cross-origin theo các quy tắc CORS. Khi đó, JavaScript không được phép đọc phản hồi từ request tương ứng.

Một số thông báo lỗi thường gặp trong Console của trình duyệt:

  • No ‘Access-Control-Allow-Origin’ header is present on the requested resource: Server không trả về header cho phép origin.
  • The value of the ‘Access-Control-Allow-Origin’ header must not be the wildcard ‘*’ when the request’s credentials mode is ‘include’: Server sử dụng wildcard trong khi request có credentials.
  • Method PUT is not allowed by Access-Control-Allow-Methods: Phương thức PUT chưa được cho phép.
  • Request header field authorization is not allowed by Access-Control-Allow-Headers: Header Authorization chưa được khai báo trong cấu hình CORS.
  • Response to preflight request doesn’t pass access control check: Phản hồi OPTIONS không đáp ứng yêu cầu CORS.

Các nguyên nhân phổ biến được tổng hợp dưới đây:

Nguyên nhânBiểu hiệnHướng xử lý
Thiếu Access-Control-Allow-OriginKhông có header cho phép originBổ sung header trên server
Origin không nằm trong danh sách cho phépWebsite frontend bị từ chốiKiểm tra lại domain frontend
Chưa xử lý OPTIONSPreflight thất bạiCấu hình endpoint OPTIONS
Thiếu phương thức HTTPPUT, DELETE bị từ chốiBổ sung Allow-Methods
Thiếu request headerAuthorization, Content-Type bị từ chốiBổ sung Allow-Headers
Cấu hình credentials saiRequest có cookie không đọc được responseKiểm tra Allow-Credentials và Allow-Origin

1. Vì sao Postman chạy được nhưng trình duyệt lại báo lỗi CORS?

Đây là tình huống khá phổ biến khi Developer kiểm tra API.

Nguyên nhân là Postman không áp dụng Same-Origin Policy và cơ chế CORS giống trình duyệt. Vì vậy, Postman có thể gửi request và đọc response từ server ngay cả khi API chưa cấu hình CORS.

Trong khi đó, trình duyệt áp dụng cơ chế kiểm tra CORS đối với các request cross-origin do ứng dụng web thực hiện.

Ví dụ:

  • Postman gửi GET đến https://api.example.com/users: API trả về dữ liệu bình thường.
  • Frontend tại https://frontend.example.com gửi GET đến cùng API: Trình duyệt không cho JavaScript đọc response nếu server không trả về header CORS phù hợp.

Vì vậy, việc API chạy thành công trên Postman không đồng nghĩa API đã cấu hình CORS đúng cho ứng dụng web.

Cần kiểm tra trực tiếp request và response trong DevTools của trình duyệt để xác định nguyên nhân.

6. Cách cấu hình và khắc phục lỗi CORS

Để khắc phục lỗi CORS, Developer cần kiểm tra nơi xử lý request và response, sau đó cấu hình HTTP Header phù hợp. Tùy kiến trúc hệ thống, CORS có thể được thiết lập tại web server như Nginx, Apache hoặc trực tiếp trong backend.

1. Cấu hình CORS trên Nginx và Apache

Cấu hình CORS trên Nginx

Nginx có thể được sử dụng làm Reverse Proxy để tiếp nhận request từ frontend và chuyển tiếp đến backend API. Developer có thể cấu hình CORS trong khối location tương ứng.

Ví dụ:

server {
    listen 80;
    server_name api.example.com;

    location /api/ {
        add_header Access-Control-Allow-Origin "https://frontend.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;

        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin "https://frontend.example.com" always;
            add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
            add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
            add_header Access-Control-Max-Age 3600 always;
            return 204;
        }

        proxy_pass http://backend;
    }
}

Trong cấu hình trên:

  • Access-Control-Allow-Origin giới hạn quyền truy cập cho frontend có origin được chỉ định.
  • Access-Control-Allow-Methods khai báo các phương thức được phép.
  • Access-Control-Allow-Headers cho phép sử dụng Authorization và Content-Type.
  • Request OPTIONS được xử lý trực tiếp và trả về mã 204.

Nếu sử dụng credentials, cần bổ sung Access-Control-Allow-Credentials: true và giữ origin cụ thể thay vì wildcard.

Lưu ý: Nginx có quy tắc kế thừa add_header theo từng cấp cấu hình. Khi khai báo lại header trong nhánh xử lý OPTIONS, cần kiểm tra để tránh response thực tế bị thiếu header.

Cấu hình CORS trên Apache

Với Apache HTTP Server, Developer có thể sử dụng module mod_headers để bổ sung các header CORS.

Ví dụ cấu hình trong VirtualHost hoặc .htaccess:

<IfModule mod_headers.c>
    Header always set Access-Control-Allow-Origin "https://frontend.example.com"
    Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Headers "Authorization, Content-Type"
</IfModule>

Nếu backend chưa xử lý OPTIONS, cần cấu hình thêm để request Preflight nhận được phản hồi phù hợp.

Khi triển khai, nên đảm bảo chỉ có một lớp chịu trách nhiệm phát sinh các header CORS. Nếu cả Apache, Nginx và backend cùng thêm Access-Control-Allow-Origin, response có thể xuất hiện nhiều giá trị không hợp lệ.

2. Cấu hình CORS trên backend với Node.js Express

Nếu backend sử dụng Node.js và Express, Developer có thể dùng middleware cors để quản lý cấu hình CORS.

Trước tiên, cài đặt thư viện:

npm install cors

Sau đó cấu hình trong ứng dụng Express:

const express = require("express");
const cors = require("cors");

const app = express();

const corsOptions = {
  origin: "https://frontend.example.com",
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Authorization", "Content-Type"],
  credentials: true
};

app.use(cors(corsOptions));

app.get("/api/users", (req, res) => {
  res.json({
    message: "CORS configured successfully"
  });
});

app.listen(3000);

Trong đó:

  • origin: Khai báo domain frontend được phép truy cập.
  • methods: Các phương thức HTTP được chấp nhận.
  • allowedHeaders: Những request header được phép sử dụng.
  • credentials: Cho phép request sử dụng credentials.

Middleware cors của Express xử lý Preflight cho các route được áp dụng khi cấu hình phù hợp.

Trong môi trường production, không nên tùy tiện sử dụng origin: “*”, đặc biệt khi ứng dụng có cơ chế xác thực bằng cookie.

Nếu cần hỗ trợ nhiều frontend, có thể sử dụng danh sách origin được phép thay vì mở quyền truy cập cho mọi domain.

3. Quy trình debug CORS bằng DevTools và curl

Khi gặp lỗi CORS, Developer nên kiểm tra request thực tế thay vì chỉ thay đổi cấu hình một cách phỏng đoán.

Bước 1: Kiểm tra request bằng DevTools

Mở Developer Tools trên Chrome hoặc trình duyệt tương tự bằng phím F12.

Truy cập tab Network, thực hiện lại thao tác gây lỗi và tìm request đến API.

Kiểm tra các thông tin:

  • Request URL: Địa chỉ API được gọi.
  • Request Method: Phương thức HTTP đang sử dụng.
  • Origin: Origin của frontend.
  • Status Code: Mã trạng thái phản hồi.
  • Response Headers: Các header CORS server trả về.

Nếu request có OPTIONS, cần kiểm tra riêng phản hồi Preflight trước khi xem xét request chính.

Bước 2: Kiểm tra response header

Đối chiếu các header server trả về với request thực tế.

Ví dụ, nếu frontend gửi request PUT có Authorization, response Preflight cần cho phép cả phương thức PUT và header Authorization.

Không nên chỉ kiểm tra Access-Control-Allow-Origin vì request vẫn có thể thất bại do thiếu Allow-Methods hoặc Allow-Headers.

Bước 3: Kiểm tra Preflight bằng curl

Developer có thể sử dụng curl để mô phỏng request OPTIONS:

curl -i -X OPTIONS \
  https://api.example.com/users/1 \
  -H "Origin: https://frontend.example.com" \
  -H "Access-Control-Request-Method: PUT" \
  -H "Access-Control-Request-Headers: authorization,content-type"

Lệnh trên giúp kiểm tra server có phản hồi các header CORS phù hợp hay không.

Lưu ý, curl chỉ gửi request và hiển thị response, không thực thi cơ chế bảo vệ CORS như trình duyệt. Vì vậy, kết quả curl thành công chưa đủ để khẳng định ứng dụng web hoạt động đúng.

Bước 4: Kiểm tra cấu hình tại từng lớp hệ thống

Nếu response vẫn sai, cần kiểm tra lần lượt:

  • Backend API có middleware CORS hay chưa?
  • Nginx hoặc Apache có ghi đè response header không?
  • Reverse Proxy có chuyển tiếp request OPTIONS không?
  • CDN hoặc Cache có trả về response cũ không?
  • Nếu response thay đổi theo Origin, cache có xử lý header Vary: Origin phù hợp không?

Thực hiện lần lượt các bước này giúp khoanh vùng nguyên nhân nhanh hơn, tránh chỉnh sửa nhiều lớp cấu hình cùng lúc.

7. Cấu hình CORS cho S3 Object Storage

CORS không chỉ được sử dụng khi kết nối giữa frontend và backend API mà còn rất quan trọng trong các ứng dụng sử dụng S3 Object Storage.

S3 Object Storage là dịch vụ lưu trữ dữ liệu dạng đối tượng, cho phép ứng dụng tải lên, tải xuống và quản lý file thông qua API. Khi trình duyệt truy cập trực tiếp bucket S3 từ một website khác origin, cấu hình CORS phù hợp sẽ giúp JavaScript đọc phản hồi từ dịch vụ lưu trữ.

1. Khi nào cần cấu hình CORS cho S3 Object Storage?

Một số trường hợp phổ biến gồm:

  • Upload file trực tiếp từ trình duyệt: Người dùng tải ảnh, video hoặc tài liệu lên bucket mà không cần chuyển dữ liệu qua backend.
  • Sử dụng Presigned URL: Backend tạo URL có chữ ký để frontend tải file trực tiếp lên hoặc xuống Object Storage.
  • Truy cập file bằng JavaScript: Ứng dụng cần đọc nội dung phản hồi từ bucket thông qua Fetch API hoặc XMLHttpRequest.
  • Lấy metadata của file: Frontend cần đọc các response header như ETag để phục vụ xử lý dữ liệu.
khi-nao-can-cau-hinh-cors-cho-s3-object-storage
Khi nào cần cấu hình Cors cho S3 Object Storage

2. Các trường cấu hình CORS trên S3

S3 hỗ trợ cấu hình CORS thông qua các trường chính:

TrườngÝ nghĩa
AllowedOriginsCác origin được phép truy cập bucket
AllowedMethodsCác phương thức HTTP được cho phép
AllowedHeadersCác request header được phép sử dụng
ExposeHeadersCác response header cho phép JavaScript đọc
MaxAgeSecondsThời gian lưu kết quả Preflight

Ví dụ cấu hình CORS cho bucket S3:

[
  {
    "AllowedOrigins": [
      "https://example.com"
    ],
    "AllowedMethods": [
      "GET",
      "PUT",
      "POST",
      "HEAD"
    ],
    "AllowedHeaders": [
      "*"
    ],
    "ExposeHeaders": [
      "ETag"
    ],
    "MaxAgeSeconds": 3600
  }
]

Trong ví dụ trên, bucket cho phép website https://example.com thực hiện các thao tác GET, PUT, POST và HEAD. Header ETag cũng được khai báo để JavaScript có thể đọc thông tin này từ response.

Trong môi trường production, nên giới hạn AllowedHeaders ở những header thực sự cần thiết thay vì sử dụng wildcard nếu không có lý do phù hợp.

3. Những lưu ý khi cấu hình CORS cho S3 Object Storage

Cấu hình CORS đúng không đồng nghĩa mọi người dùng đều có quyền truy cập dữ liệu trong bucket.

Cần phân biệt ba cơ chế:

  • CORS: Kiểm soát việc trình duyệt cho phép JavaScript đọc phản hồi từ tài nguyên khác origin.
  • Bucket Policy và IAM: Kiểm soát quyền truy cập dữ liệu và thao tác trên bucket.
  • Presigned URL: Cung cấp quyền truy cập có thời hạn thông qua URL được ký sẵn.

Ví dụ, một bucket được đặt ở chế độ private vẫn có thể cấu hình CORS cho frontend sử dụng Presigned URL. Tuy nhiên, người dùng vẫn phải có URL hợp lệ và quyền truy cập tương ứng mới có thể thực hiện thao tác với file.

Ngoài ra, cú pháp và mức độ hỗ trợ CORS có thể khác nhau giữa các nhà cung cấp S3 Compatible Object Storage. Developer cần đối chiếu tài liệu kỹ thuật của dịch vụ đang sử dụng trước khi triển khai.

Nếu ứng dụng thường xuyên xử lý ảnh, video hoặc tài liệu trực tiếp từ trình duyệt, việc lựa chọn dịch vụ S3 Object Storage hỗ trợ API tương thích và cấu hình CORS linh hoạt sẽ giúp đơn giản hóa kiến trúc hệ thống.

Doanh nghiệp có thể tham khảo thuê S3 Object Storage của LANIT để triển khai lưu trữ dữ liệu dạng đối tượng cho ứng dụng web, hệ thống quản lý file và các nền tảng số.

8. Những rủi ro bảo mật khi cấu hình CORS

CORS giúp kiểm soát quyền truy cập tài nguyên cross-origin nhưng cấu hình không đúng có thể làm tăng nguy cơ lộ dữ liệu hoặc tạo điều kiện cho các website không đáng tin cậy truy cập thông tin nhạy cảm.

Dưới đây là những rủi ro Developer cần chú ý.

1. Sử dụng wildcard (*) không đúng cách

Wildcard * trong Access-Control-Allow-Origin cho phép mọi origin truy cập và đọc phản hồi trong những trường hợp không sử dụng credentials.

Nếu API chứa dữ liệu công khai, cách cấu hình này có thể phù hợp. Tuy nhiên, với API chứa thông tin cá nhân hoặc dữ liệu nội bộ, việc mở quyền cho mọi origin có thể làm tăng nguy cơ dữ liệu bị các website không mong muốn đọc.

Cách hạn chế: Chỉ khai báo những domain thực sự cần truy cập, đặc biệt với API có dữ liệu nhạy cảm.

2. Phản chiếu Origin mà không kiểm tra

Một lỗi phổ biến là server tự động lấy giá trị Origin từ request và trả lại trong Access-Control-Allow-Origin mà không kiểm tra tính hợp lệ.

Ví dụ:

const origin = req.headers.origin;

res.setHeader(
  "Access-Control-Allow-Origin",
  origin
);

Nếu không có bước xác minh, website không đáng tin cậy cũng có thể được server chấp nhận.

Cách hạn chế: Xây dựng danh sách Allowlist gồm các origin được phép. Server cần kiểm tra Origin trước khi phản hồi header CORS.

3. Cho phép Origin null thiếu kiểm soát

Giá trị Origin: null có thể xuất hiện trong một số ngữ cảnh như tài liệu sử dụng URL scheme file:, tài liệu sandbox hoặc các origin opaque.

Nếu server cho phép null một cách tùy tiện, những ngữ cảnh không đáng tin cậy có thể được cấp quyền đọc dữ liệu ngoài dự kiến.

Cách hạn chế: Chỉ cho phép origin null khi có nhu cầu cụ thể và đã đánh giá rủi ro. Không nên đưa giá trị này vào danh sách cho phép mặc định.

4. Cho phép credentials với origin không đáng tin cậy

Khi cấu hình Access-Control-Allow-Credentials: true, trình duyệt có thể chia sẻ phản hồi của request có credentials nếu các điều kiện CORS khác cũng được đáp ứng.

Nếu kết hợp với cơ chế kiểm tra Origin lỏng lẻo, website không đáng tin cậy có thể đọc dữ liệu gắn với phiên đăng nhập của người dùng trong những điều kiện phù hợp.

Cách hạn chế:

  • Chỉ cho phép credentials đối với những origin đã xác minh.
  • Không phản chiếu Origin một cách tự động.
  • Kiểm tra thuộc tính cookie như SameSite và Secure.
  • Tiếp tục triển khai xác thực, phân quyền và các biện pháp chống CSRF phù hợp.

Cần nhớ rằng CORS không thay thế xác thực, phân quyền hay CSRF protection. Đặc biệt, CORS không đảm bảo một request cross-origin sẽ không được gửi đến server.

>>>> Đọc thêm bài viết: CSRF là gì? Hướng dẫn chi tiết cách phòng chống tấn công CSRF

9. Câu hỏi thường gặp về CORS

1. CORS có phải là lỗi của server không?

Không hoàn toàn. Lỗi CORS xuất hiện khi trình duyệt không thể xác nhận quyền truy cập tài nguyên cross-origin theo quy tắc CORS. Nguyên nhân thường liên quan đến cấu hình HTTP Header trên server, nhưng cũng có thể do Reverse Proxy, CDN hoặc cách triển khai request từ frontend.

2. CORS có ảnh hưởng đến SEO không?

CORS không phải yếu tố xếp hạng SEO trực tiếp. Tuy nhiên, nếu website sử dụng API khác origin để tải nội dung, hình ảnh hoặc dữ liệu quan trọng mà cấu hình CORS không đúng, một số chức năng có thể không hoạt động như mong muốn. Điều này có thể gián tiếp ảnh hưởng đến trải nghiệm người dùng và khả năng hiển thị nội dung.

3. Tại sao Postman không bị lỗi CORS nhưng trình duyệt lại bị?

Postman không áp dụng Same-Origin Policy và cơ chế CORS giống trình duyệt. Vì vậy, Postman vẫn có thể gửi request và đọc response ngay cả khi API chưa cấu hình CORS. Trình duyệt thì kiểm tra các HTTP Header CORS trước khi cho phép JavaScript đọc phản hồi từ tài nguyên khác origin.

4. CORS có áp dụng cho Mobile App không?

CORS chủ yếu là cơ chế bảo mật của trình duyệt. Các ứng dụng mobile native sử dụng HTTP client riêng thường không bị áp dụng CORS theo cách giống trình duyệt. Tuy nhiên, nếu ứng dụng sử dụng WebView hoặc môi trường web nhúng, các quy tắc CORS vẫn có thể được áp dụng.

5. CORS và CSP khác nhau như thế nào?

CORS và CSP (Content Security Policy) đều là cơ chế bảo mật liên quan đến ứng dụng web nhưng có mục đích khác nhau.

Tiêu chíCORSCSP
Tên đầy đủCross-Origin Resource SharingContent Security Policy
Mục đíchKiểm soát quyền đọc tài nguyên cross-originHạn chế những tài nguyên mà trang web được phép tải hoặc thực thi
Cấu hìnhHTTP Response Header CORSHTTP Header Content-Security-Policy hoặc thẻ meta
Phạm viChủ yếu kiểm soát quyền chia sẻ phản hồi cross-originKiểm soát nguồn script, style, image, frame và nhiều loại tài nguyên khác

Có thể hiểu đơn giản, CORS kiểm soát việc chia sẻ tài nguyên giữa các origin, còn CSP giúp website giới hạn những nguồn nội dung được phép tải và thực thi.

6. Có thể tắt CORS trên trình duyệt không?

Một số trình duyệt cho phép chạy trong chế độ đặc biệt để vô hiệu hóa hoặc thay đổi cơ chế bảo vệ liên quan đến CORS nhằm phục vụ thử nghiệm. Tuy nhiên, đây không phải giải pháp khắc phục lỗi phù hợp khi triển khai ứng dụng thực tế.

Việc tắt CORS có thể khiến trình duyệt mất đi một lớp bảo vệ quan trọng. Developer nên xử lý cấu hình tại server, backend hoặc Reverse Proxy thay vì yêu cầu người dùng thay đổi thiết lập bảo mật của trình duyệt.

CORS là cơ chế quan trọng trong phát triển ứng dụng web, giúp kiểm soát việc chia sẻ tài nguyên giữa các origin thông qua HTTP Header. Hiểu rõ Same-Origin Policy, Simple Request, Preflight Request và các header liên quan sẽ giúp Developer chủ động cấu hình API, xử lý lỗi kết nối và hạn chế những rủi ro bảo mật.

Đánh giá bài viết
Với nhiều năm kinh nghiệm trong lĩnh vực công nghệ thông tin, đặc biệt là hạ tầng máy chủ, hệ thống mạng và bảo mật, Nguyễn Đức Hòa hiện đã đảm nhận vai trò Trưởng phòng Kỹ thuật tại công ty LANIT – đơn vị cung cấp giải pháp công nghệ tiên tiến cho doanh nghiệp. Anh không chỉ là người trực tiếp xây dựng, phát triển và vận hành các hệ thống kỹ thuật phức tạp mà còn là người truyền lửa và định hướng chiến lược kỹ thuật cho toàn bộ đội ngũ của LANIT. Tư vấn duy duy hệ thống Chắc chắn, khả năng xử lý sự cố nhanh nhạy cảm cùng tầm nhìn phát triển thời hạn đã giúp anh trở thành một trong những nhân sự chủ yếu trong quá trình mở rộng hành động theo mô hình dịch vụ công nghệ của LANIT.
Chat với chúng tôi qua Zalo!
Chat với chúng tôi qua Zalo!