Vì sao phải dùng cả bảo vệ CSRF lẫn CORS?
(smagin.fyi)- 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
SameSitemới được thêm vào, và giá trị mặc định được đổi thànhLax - 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)
- Origin: tổ hợp
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
OPTIONStrướ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:
fetchvàXMLHttpRequest- web font
- texture WebGL
- khung hình ảnh/video được vẽ bằng
drawImagetrongcanvas - 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-Originnế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
- Thẻ
Câu hỏi: Chính sách
SameSitevà 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=Laxhay 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."
1 bình luận
Ý 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
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
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
fetch()nếu chỉ dùng các header được cho phépCó vẻ có một lời giải thích tốt hơn về chủ đề này
Phản hồi cho câu hỏi trong bài blog
<form>của HTML 4.0 có thể gửi simple request tới bất kỳ origin nàoNă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"
Việc SameSite được thêm vào độc lập với CORS preflight gây khó hiểu
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
Câu hỏi về lý do CSRF token được xoay vòng
Yêu cầu một lưu đồ cho chủ đề phức tạp này
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
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
Sự nhầm lẫn về bảo vệ CSRF