Skip to content

Zoom-in: CORS

Karify98·
Cover Image for 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.

Vấn đề còn lại: các ứng dụng web hiện đại bắt buộc phải gọi API từ nhiều origin khác nhau (như CDN, dịch vụ bên thứ ba), do đó cần một cách để nới lỏng chính sách SOP một cách an toàn.

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ụ: OriginAccess-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ự.

Vấn đề còn lại: server xử lý thành công không có nghĩa là trình duyệt sẽ cho phép ứng dụng client đọc kết quả trả về.

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