CSRF — Cross-Site Request Forgery và Chiến Lược Bảo Vệ
Khám phá bản chất tấn công CSRF, mô hình Synchronizer Token, Double-Submit Cookie và lý do stateless JWT API có thể vô hiệu hóa CSRF an toàn.
TL;DR: CSRF (Cross-Site Request Forgery) là kỹ thuật tấn công lợi dụng hành vi tự nhiên của trình duyệt: tự động đính kèm Cookie tương ứng với Origin đích, ngay cả khi yêu cầu xuất phát từ một trang web độc hại khác. Giải pháp phòng vệ cốt lõi là Synchronizer Token Pattern — máy chủ nhúng một mã token ngẫu nhiên vào form HTML và chỉ chấp nhận yêu cầu khi token gửi lên khớp với token lưu trong phiên. Đối với kiến trúc REST API phi trạng thái (Stateless) xác thực qua header Authorization: Bearer <token>, trình duyệt không bao giờ tự động gửi header này giữa các trang, do đó bề mặt tấn công CSRF không tồn tại và việc tắt CSRF là hoàn toàn an toàn.
Tại sao tấn công CSRF lại nguy hiểm đối với ứng dụng web?
Hãy tưởng tượng hệ thống TaskFlow có endpoint chuyển giao quyền quản trị dự án:
POST /api/projects/42/transfer HTTP/1.1
Host: taskflow.io
Content-Type: application/x-www-form-urlencoded
[email protected]
Ứng dụng sử dụng Session Cookie để nhận diện người dùng. Cookie được đánh dấu HttpOnly nên JavaScript không thể đọc được. Kịch bản tấn công diễn ra như sau:
- Người dùng quản trị đăng nhập vào TaskFlow và giữ phiên làm việc trong trình duyệt.
- Quản trị viên vô tình truy cập vào trang web giả mạo
https://evil.com. - Trang web độc hại chứa một biểu mẫu HTML ẩn tự động submit ngay khi tải:
<form id="csrfForm" action="https://taskflow.io/api/projects/42/transfer" method="POST">
<input type="hidden" name="newOwner" value="[email protected]" />
</form>
<script>document.getElementById('csrfForm').submit();</script>
- Trình duyệt gửi yêu cầu POST đến
taskflow.iovà tự động đính kèm Session Cookie của nạn nhân. - Máy chủ kiểm tra thấy Cookie hợp lệ và chuyển quyền dự án cho kẻ tấn công mà người dùng không hề hay biết.
Same-Origin Policy (SOP) không thể bảo vệ bạn trong tình huống này vì SOP chỉ chặn việc JavaScript đọc nội dung phản hồi, chứ không chặn trình duyệt gửi yêu cầu đi.
Cơ chế trình duyệt tự động đính kèm cookie hoạt động ra sao?
Để hiểu tại sao CSRF xuất hiện, cần phân biệt phạm vi gắn cookie: trình duyệt gắn cookie dựa trên tên miền đích của yêu cầu (Request Destination), hoàn toàn không phụ thuộc vào trang web hiện tại đang kích hoạt yêu cầu đó. Khi evil.com gửi form tới taskflow.io, trình duyệt đối soát Cookie Jar theo host taskflow.io và tự động gắn mọi cookie phù hợp vào header Cookie.
Đây là tính năng chủ ý được thiết kế từ những ngày đầu của web nhằm hỗ trợ chuyển hướng giữa các website (ví dụ thanh toán cổng ngân hàng). Kẻ tấn công không cần đọc kết quả trả về: thiệt hại đã hoàn tất ngay khi máy chủ xử lý xong yêu cầu.
Synchronizer Token Pattern ngăn chặn tấn công như thế nào?
Synchronizer Token Pattern là cơ chế phòng thủ kinh điển: máy chủ tạo ra một mã token ngẫu nhiên, lưu trữ trong phiên làm việc của người dùng (HttpSession), đồng thời nhúng mã này vào biểu mẫu HTML.
<form action="/projects/42/transfer" method="POST">
<input type="hidden" name="_csrf" value="b47a9f1c-token-bi-mat-tu-server" />
<input type="text" name="newOwner" />
<button type="submit">Chuyển quyền</button>
</form>
Kẻ tấn công tại evil.com không thể giả mạo token này vì muốn lấy được token, trang web độc hại phải đọc nội dung HTML từ taskflow.io. Thao tác đọc chéo này bị Same-Origin Policy chặn đứng hoàn toàn:

Spring Security tự động kích hoạt Synchronizer Token theo mặc định:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin(Customizer.withDefaults()) // CSRF tu dong bat
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
return http.build();
}
Double-Submit Cookie Pattern được áp dụng khi nào cho SPA?
Đối với các ứng dụng Single Page Application (SPA) không muốn lưu trạng thái session phía máy chủ nhưng vẫn dùng cookie xác thực, mô hình Double-Submit Cookie là giải pháp thay thế:
- Máy chủ cấp một cookie chứa mã CSRF ngẫu nhiên và đặt thuộc tính
HttpOnly = false. - Ứng dụng JavaScript trên frontend đọc giá trị cookie này và sao chép vào header HTTP tùy biến:
X-XSRF-TOKEN. - Khi nhận yêu cầu ghi, máy chủ so sánh giá trị trong cookie và giá trị trong header. Nếu trùng khớp, yêu cầu được chấp thuận.
Trang web độc hại không thể tấn công vì dù trình duyệt có tự gửi cookie, script độc hại tại origin khác không thể đọc cookie để thiết lập header tùy biến tương ứng.
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
Thuộc tính SameSite của Cookie đóng vai trò phòng thủ ra sao?
Thuộc tính SameSite trên HTTP Cookie cho phép máy chủ chỉ thị cho trình duyệt khi nào được phép gửi cookie trong các ngữ cảnh cross-site:
| Giá trị SameSite | Hành vi gửi Cookie | Mức độ phòng vệ CSRF |
|---|---|---|
Strict | Chỉ gửi khi yêu cầu xuất phát từ chính trang web đó | Cao nhất (Ngăn chặn 100% CSRF) |
Lax | Gửi trong cùng trang và khi click link GET thông thường | Mặc định trên trình duyệt hiện đại |
None | Gửi trong mọi trường hợp (Bắt buộc kèm cờ Secure) | Không bảo vệ |
Cấu hình trong file application.yml:
server:
servlet:
session:
cookie:
same-site: lax
secure: true
http-only: true
Mặc dù SameSite=Lax chặn hầu hết các form POST cross-site, hệ thống vẫn cần token CSRF để phòng thủ theo chiều sâu (Defense in Depth) trước các trình duyệt cũ hoặc các endpoint GET vi phạm nguyên tắc bị khai thác.
Khi nào lập trình viên có thể tắt hoàn toàn CSRF an toàn?
Trong các cấu hình Spring Security cho REST API, dòng lệnh sau rất phổ biến:
http.csrf(csrf -> csrf.disable());
Việc tắt CSRF chỉ an toàn khi và chỉ khi ứng dụng thỏa mãn điều kiện: Không sử dụng cookie hoặc cơ chế xác thực tự động gửi bởi trình duyệt.
Khi ứng dụng SPA xác thực qua JWT truyền qua header Authorization: Bearer <token>:
- Trình duyệt không bao giờ tự động đính kèm header
Authorizationkhi người dùng mở trang web khác. - Mã JavaScript độc hại trên
evil.comkhông thể đọc token nằm trong bộ nhớ của trang web gốc. - Bề mặt tấn công CSRF hoàn toàn bị triệt tiêu theo thiết kế.
| Kiến trúc hệ thống | Phương thức xác thực | Cấu hình CSRF |
|---|---|---|
| REST API Stateless | Header Authorization: Bearer <token> | Tắt (disable()) |
| Web MVC truyền thống | Session Cookie (JSESSIONID) | Bật (Synchronizer Token) |
| SPA dùng Cookie JWT | Cookie HttpOnly chứa JWT | Bật (CookieCsrfTokenRepository) |
CORS và CSRF khác biệt nhau về bản chất và mục tiêu như thế nào?
| Tiêu chí | CORS | CSRF |
|---|---|---|
| Bản chất | Cơ chế nới lỏng Same-Origin Policy do trình duyệt quản lý | Kỹ thuật tấn công mượn quyền người dùng |
| Mục tiêu can thiệp | Chặn JavaScript đọc trộm dữ liệu phản hồi (Response) | Ngăn kẻ gian thực thi lệnh thay đổi trạng thái ngầm |
| Kích hoạt | Máy chủ cấu hình các header Access-Control-* | Máy chủ thẩm định tính hợp lệ của token đính kèm |
| Phụ thuộc lẫn nhau | Cấu hình CORS không giúp ngăn chặn tấn công CSRF | Tắt CSRF không gây ảnh hưởng đến chính sách CORS |
Vị trí và cơ chế hoạt động của CsrfFilter trong chuỗi lọc là gì?
CsrfFilter nằm ở vị trí rất sớm trong SecurityFilterChain, đứng trước AuthorizationFilter:

- Đối với các phương thức an toàn (
GET,HEAD,OPTIONS,TRACE): Bộ lọc bỏ qua kiểm tra và chỉ nạp token vào request attribute. - Đối với các phương thức thay đổi dữ liệu (
POST,PUT,DELETE,PATCH): Bộ lọc đọc token từ form param_csrfhoặc headerX-XSRF-TOKEN. Nếu thiếu hoặc sai lệch, lập tức trả về mã lỗi403 Forbiddenmà không cần gọi tiếp đếnAuthorizationFilter.
Những điểm then chốt cần ghi nhớ là gì?
- CSRF lợi dụng đặc tính tự động gửi cookie của trình duyệt để thực thi thao tác mạo danh.
- Same-Origin Policy ngăn việc đọc phản hồi chứ không ngăn việc gửi yêu cầu đi.
- Sử dụng Synchronizer Token Pattern cho ứng dụng Web MVC có phiên làm việc dạng session.
- Sử dụng Double-Submit Cookie Pattern cho Single Page Application dùng Cookie xác thực.
- Hoàn toàn an toàn khi tắt CSRF đối với REST API phi trạng thái sử dụng JWT qua Authorization header.
Bạn đã làm chủ cơ chế phòng thủ CSRF trong Spring Security chưa?
- Q1Tại sao Same-Origin Policy (SOP) không thể bảo vệ ứng dụng khỏi các cuộc tấn công CSRF cơ bản?
- Q2Tại sao việc tắt CSRF (csrf.disable()) lại an toàn đối với các REST API sử dụng Bearer Token trong Authorization header?
- Q3Trong kiến trúc SPA sử dụng Double-Submit Cookie Pattern, tại sao Cookie chứa mã CSRF lại phải được cấu hình HttpOnly = false?
- Q4Thuộc tính SameSite=Lax trên cookie phiên bảo vệ người dùng trước tấn công CSRF như thế nào?
- Q5Nếu ứng dụng web MVC gặp lỗi 403 Forbidden ngay khi submit một form POST, nguyên nhân phổ biến nhất về cấu hình bảo mật là gì?
Bài tiếp theo: Mini-challenge — Bảo Vệ TaskFlow với JWT và Phân Quyền
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
CORS — Same-Origin Policy, Preflight và Spring Security
Bài tiếpMini-challenge: TaskFlow v3 — JWT + role-based access control
Lộ trình Junior Java Backend Developer
Chặng 18/21 · Authentication & Authorization — bài 17/19 của chặng
Thảo luận & góp ý
Hỏi đáp về bài này