Zoom-in: CORS

Gửi một API request từ ứng dụng client đến một server khác domain, và bảng console lập tức hiện thông báo lỗi đỏ rực.
graph LR
C(["💻 Client\napp.com"]) -->|"API Request"| S(["🖥️ Server\napi.com"])
S -->|"Response"| C
style C fill:#1e3a5f,stroke:#3b82f6,color:#93c5fd
style S fill:#1a3a2a,stroke:#22c55e,color:#86efac
Phóng to dần vào đó.
Layer 1 — Same-Origin Policy: Vách ngăn mặc định của trình duyệt
Để hiểu tại sao lỗi xảy ra, cần hiểu chính sách bảo mật cơ bản nhất của trình duyệt: Same-Origin Policy (SOP).
Theo SOP, trình duyệt ngăn chặn mã kịch bản (script) của một trang web đọc dữ liệu nhạy cảm từ một trang web khác có origin khác (khác biệt về protocol, domain, hoặc port).
Nếu không có SOP, một kịch bản độc hại chạy trên evil.com có thể gửi request đến ngân hàng của bạn (bank.com/api/balance). Vì trình duyệt tự động đính kèm cookie đăng nhập vào request gửi đến bank.com, kẻ tấn công sẽ đọc được toàn bộ số dư và thông tin cá nhân của bạn một cách dễ dàng.
Layer 2 — Preflight Request: Thăm dò trước khi hành động
CORS ra đời như một cơ chế nới lỏng SOP. Khi ứng dụng client thực hiện một request phi tiêu chuẩn (như dùng method PUT, DELETE, hoặc thêm custom header), trình duyệt sẽ tự động gửi một request thăm dò gọi là Preflight Request.
sequenceDiagram
participant B as Trình duyệt (app.com)
participant S as Server (api.com)
B->>S: 1. OPTIONS /data (Preflight)
S-->>B: 2. 204 No Content (Chấp nhận origin app.com)
B->>S: 3. PUT /data (Request thật)
S-->>B: 4. 200 OK (Kết quả)
Preflight Request sử dụng method OPTIONS. Nó chứa các header mô tả chi tiết về request thực tế sắp gửi đi, ví dụ: Origin và Access-Control-Request-Method.
Mục đích là để hỏi server: "Tôi chuẩn bị gửi một request PUT từ origin app.com, server có đồng ý xử lý không?". Server kiểm tra và trả về phản hồi chấp thuận hoặc từ chối trước khi trình duyệt cho phép gửi dữ liệu thật sự.
Layer 3 — Browser Enforcement: Trình duyệt thực thi, không phải server
Một hiểu lầm rất phổ biến là khi gặp lỗi CORS, nghĩa là server đã chặn request. Thực tế hoàn toàn ngược lại: server vẫn nhận, xử lý dữ liệu và trả về kết quả bình thường. Lỗi CORS xảy ra ở client-side do trình duyệt chặn lại.
sequenceDiagram
participant B as Trình duyệt (app.com)
participant S as Server (api.com)
B->>S: GET /profile (Request)
S-->>B: 200 OK + Data (Response không có CORS header)
Note over B: Phát hiện thiếu header!<br/>Chặn không cho script đọc Data.
Khi server phản hồi, trình duyệt sẽ kiểm tra xem response có chứa header Access-Control-Allow-Origin khớp với origin của client hay không. Nếu thiếu hoặc không khớp, trình duyệt lập tức chặn kết quả trả về và ném ra lỗi console.
Vì vậy, giải pháp cho CORS luôn là cấu hình server trả về đúng các header tương tác, chứ không thể sửa đổi ở phía client.
Full picture
sequenceDiagram
participant B as Trình duyệt (app.com)
participant S as Server (api.com)
alt Request tiêu chuẩn (Simple Request)
B->>S: GET /public-data
S-->>B: 200 OK + Access-Control-Allow-Origin: *
Note over B: Hợp lệ, hiển thị dữ liệu
else Request phi tiêu chuẩn (Cần Preflight)
B->>S: OPTIONS /update (Origin: app.com)
alt Server đồng ý
S-->>B: 204 No Content (Access-Control-Allow-Origin: app.com)
B->>S: PUT /update
S-->>B: 200 OK + Dữ liệu
else Server từ chối
S-->>B: 400 Bad Request / 204 Không cho phép origin
Note over B: Chặn không gửi PUT, báo lỗi CORS
end
end
Takeaway
CORS không phải là lỗi của server và cũng không phải là công cụ bảo mật để bảo vệ server khỏi bị tấn công. CORS là một cơ chế do trình duyệt thực thi nhằm bảo vệ chính người dùng cuối khỏi bị rò rỉ dữ liệu nhạy cảm qua Same-Origin Policy. Khi gặp lỗi CORS, hãy kiểm tra cấu hình header ở phía server tiếp nhận request, thay vì cố gắng tinh chỉnh mã nguồn client.
Bài viết được hỗ trợ bởi Amy 🌸 - AI Assistant. Nội dung đã được kiểm duyệt bởi tác giả.
Related Posts
Zoom-in: WebSocket
Chat app cập nhật tin nhắn tức thời mà không cần reload trang. Cách WebSocket giải thoát ứng dụng khỏi giới hạn một chiều của HTTP.
Zoom-in: OAuth 2.0
'Đăng nhập bằng Google' ẩn sau đó một cơ chế ủy quyền mà không bao giờ để password rời khỏi Google. OAuth 2.0 giải quyết bài toán delegation mà không hi sinh bảo mật.
Zoom-in: JWT
Gọi API, server cho qua. Bên trong token đó là chữ ký số — không phải mã hóa, và đó là điều quan trọng nhất cần hiểu.