33 điểm bởi GN⁺ 2025-03-04 | 1 bình luận | Chia sẻ qua WhatsApp
  • Khi tìm hiểu về Cross-Site Request, ban đầu tôi không hiểu vì sao lại cần cả bảo vệ CSRF lẫn CORS. Nhưng để giải thích điều này cần khá nhiều lời.

CSRF và CORS

  • CSRF (Cross-Site Request Forgery)
    • Trước đây khá phổ biến, nhưng hiện nay hầu hết các web framework đều cung cấp cơ chế bảo vệ mặc định nên gần như không còn là vấn đề lớn
    • Cách tấn công: dụ người dùng nhấp vào một biểu mẫu nào đó trên trang độc hại để gửi một yêu cầu cross-site cụ thể
    • Cách phòng vệ: xác minh xem yêu cầu có đến từ trang khác hay không
  • CORS (Cross-Origin Resource Sharing)
    • Là một phần của đặc tả HTTP, định nghĩa cách cho phép một số yêu cầu cross-site nhất định
    • Dùng preflight request và các response header để chỉ định origin nào được phép gửi yêu cầu

Vậy thì yêu cầu cross-site vốn được cho phép mặc định nên mới cần bảo vệ CSRF, hay vốn bị chặn mặc định nên cần CORS để cho phép? Đáp án là cả hai.

Cơ chế hoạt động mặc định

  • Same-origin policy
    • Là chính sách bảo mật do trình duyệt áp đặt
    • Nói chung cho phép ghi (Write) cross-site, nhưng cấm đọc (Read)
    • Ví dụ, trình duyệt cho phép gửi yêu cầu POST qua form, nhưng không cho đọc phản hồi
  • Chính sách cookie SameSite
    • Năm 2019, hành vi mặc định của cookie đã thay đổi
    • Trước đây, cookie luôn được gửi cả trong các yêu cầu cross-site
    • Thuộc tính SameSite mới được thêm vào, và giá trị mặc định được đổi thành Lax
    • Tính đến năm 2025, 96% trình duyệt hỗ trợ thuộc tính SameSite, 75% hỗ trợ giá trị mặc định mới (Lax)
    • Tuy nhiên Safari không áp dụng điều này làm mặc định, và UCBrowser vẫn chưa hỗ trợ
  • Sự khác biệt giữa Site và Origin
    • Origin: tổ hợp giao thức + tên máy chủ + cổng
    • Site: tổ hợp giao thức + top-level domain + 1 (bỏ qua subdomain và cổng)

CORS

  • CORS là cách nới lỏng same-origin policy như một ngoại lệ cho một số origin nhất định
  • Trình duyệt gửi một preflight request kiểu OPTIONS trước khi gửi yêu cầu chính
  • Máy chủ định nghĩa quy tắc cho phép qua response header (dùng các header Access-Control-*)
  • Các loại yêu cầu áp dụng CORS:
    • fetchXMLHttpRequest
    • web font
    • texture WebGL
    • khung hình ảnh/video được vẽ bằng drawImage trong canvas
    • hình ảnh dùng trong thuộc tính CSS shape-outside
  • Tuy nhiên, việc submit form là ngoại lệ và không áp dụng CORS
    • Thẻ <form> của HTML 4.0 từ rất lâu trước đây đã cho phép yêu cầu cross-site
    • Vì vậy các máy chủ cũ lẽ ra đã phải được thiết kế để chống lại tấn công CSRF
    • Máy chủ phải thiết lập Access-Control-Allow-Origin nếu muốn chia sẻ phản hồi, nhưng bản thân yêu cầu vẫn được chấp nhận ngay cả khi không có preflight request

Câu hỏi: Chính sách SameSite và cơ chế này duy trì tính nhất quán với nhau như thế nào?

Cách bảo vệ CSRF

  • Các yêu cầu ghi cross-site được phép, nhưng phản hồi thì không được chia sẻ
    • Hầu hết website không muốn cho phép ghi cross-site
  • Phương pháp phòng thủ CSRF tiêu chuẩn
    • Bao gồm CSRF token theo từng người dùng trong yêu cầu để xác minh
    • Cách làm:
      • Submit form: thêm token vào hidden input
      • Yêu cầu JS: lưu vào cookie hoặc thẻ meta, rồi đưa vào request header hoặc tham số
  • Các yêu cầu JS vốn mặc định bị chặn nếu là cross-site
    • Nhưng vẫn được phép nếu là same-site request
    • Nếu thêm CSRF token, có thể xác minh mọi yêu cầu theo cùng một cách
  • Lợi ích bảo mật bổ sung
    • Hoạt động dựa trên giả định rằng trình duyệt mặc định phải chặn việc đọc phản hồi
    • An toàn hơn so với việc kiểm tra header Origin

Câu hỏi: Trong một số framework, CSRF token được thay đổi định kỳ. Vì sao lại như vậy?

Vai trò của trình duyệt

  • Cốt lõi của bảo mật web phụ thuộc vào việc có thể tin cậy trình duyệt hay không
  • Trình duyệt sẽ:
    • Thực thi same-origin policy
    • Chặn không cho đọc phản hồi nếu không được phép
    • Quyết định có áp dụng mặc định SameSite=Lax hay không
    • Triển khai CORS và gửi preflight request an toàn

Chúng ta phải tin cậy trình duyệt mình đang dùng.

Kết luận

  • Nếu SameSite=Lax được hỗ trợ trên 100% trình duyệt thì bảo mật sẽ được tăng cường hơn, nhưng
    hiện tại vẫn còn tình huống chỉ riêng các yêu cầu POST cross-site được cho phép như một ngoại lệ
  • Vì vậy nhà phát triển vẫn phải tiếp tục cân nhắc bảo vệ CSRF

"Internet đang ngày càng an toàn hơn, nhưng đồng thời khả năng tương thích ngược với quá khứ cũng đang dần giảm đi."

Nguồn

  1. Same-origin policy
  2. caniuse SameSite cookie attribute
  3. OWASP CSRF cheatsheet
  4. CORS wiki with requirements
  5. CORS spec
  6. CORS on MDN
  7. Preflight request
  8. Origin request header
  9. Origin and Site

1 bình luận

 
GN⁺ 2025-03-04
Ý kiến trên Hacker News
  • CORS là cơ chế để máy chủ nói rõ với trình duyệt rằng những yêu cầu cross-origin nào được phép đọc phản hồi

    • Về mặc định, trình duyệt chặn script cross-origin đọc phản hồi
    • Nếu không được cho phép rõ ràng, miền gửi yêu cầu sẽ không thể đọc phản hồi
    • Ví dụ, một script từ evil.com có thể gửi yêu cầu tới bank.com/transactions để đọc lịch sử giao dịch của nạn nhân
    • Trình duyệt cho phép yêu cầu tới được bank.com, nhưng chặn evil.com đọc phản hồi
  • Bảo vệ CSRF ngăn các yêu cầu cross-origin độc hại thực hiện hành động trái phép thay mặt cho người dùng đã được xác thực

    • Ví dụ, script từ evil.com có thể gửi yêu cầu để thực hiện hành động trên bank.com (ví dụ: chuyển tiền qua bank.com/transfer?from=victim&to=hacker)
    • Cơ chế bảo vệ CSRF phía máy chủ của bank.com sẽ từ chối việc này (có thể vì yêu cầu không chứa CSRF token bí mật)
  • Bảo vệ CSRF là để bảo vệ ghi, còn CORS là để bảo vệ đọc

  • Các yêu cầu do JS khởi tạo về mặc định không được phép cross-site

    • Có thể khởi tạo yêu cầu cross-site bằng fetch() nếu chỉ dùng các header được cho phép
  • Có vẻ có một lời giải thích tốt hơn về chủ đề này

    • Có cung cấp liên kết blog liên quan
  • Phản hồi cho câu hỏi trong bài blog

    • Phần tử <form> của HTML 4.0 có thể gửi simple request tới bất kỳ origin nào
    • Có câu hỏi về việc điều này khớp với sáng kiến SameSite như thế nào
  • Năm 2022, đã thêm một đoạn vào bài viết MDN về CORS để làm rõ nguồn gốc của thuật ngữ "simple request"

    • Trước đó chỉ nói rằng nó không được đề cập trong đặc tả fetch
    • Năm 2019 chưa nhắc tới việc các biện pháp chống CSRF của trình duyệt hỗ trợ hoặc mặc định dùng SameSite=Lax
  • Việc SameSite được thêm vào độc lập với CORS preflight gây khó hiểu

    • Thắc mắc vì sao các nhà làm trình duyệt không yêu cầu preflight cho mọi yêu cầu POST cross-origin
  • Có thể nghĩ rằng không dùng csrf vẫn an toàn, nhưng một số thư viện (ví dụ: django rest framework) có thể xử lý HTML form nếu header content-type được thiết lập

    • Điều này cho phép đăng form tới trang của người dùng để gửi yêu cầu thay mặt họ
  • Câu hỏi về lý do CSRF token được xoay vòng

    • OWASP nói rằng như vậy an toàn hơn, nhưng không rõ lý do
  • Yêu cầu một lưu đồ cho chủ đề phức tạp này

    • Muốn có một nền tảng ứng dụng và bộ tiêu chuẩn mới
  • Những cơ chế này không hỗ trợ việc lần vết chẩn đoán một cách dễ dàng

    • Đã nhiều lần gặp lỗi mơ hồ với các trường hợp sử dụng hợp lệ nhưng cấu hình chưa đúng
  • Không hiểu vì sao trước khi có CORS, vẫn có thể gửi yêu cầu tới endpoint bất kỳ không cùng origin với trang nhưng lại không thể xem phản hồi

    • Thắc mắc liệu đây có phải là thứ vô tình có trong đặc tả, hay được thiết kế có chủ đích để dự liệu XSS, hay do một trình duyệt tiên phong làm vậy rồi các trình duyệt khác làm theo
  • Sự nhầm lẫn về bảo vệ CSRF

    • Thắc mắc cơ chế nào ngăn kẻ tấn công lấy CSRF token từ goodsite.com, đưa nó vào badsite.com, rồi lừa Alice gửi yêu cầu từ badsite.com tới goodsite.com