Câu hỏi CSRF token còn cần không nếu đã có SameSite cookie? Bài này phân tích thực tế, kèm code Node.js implement double submit + synchronizer pattern.

CSRF là gì?
Cross-Site Request Forgery: attacker dụ user click link/form ở evil.com, browser tự gửi cookie auth của victim đến alodev.vn -> action thực hiện thay user.
html<!-- evil.com -->
<form action="https://alodev.vn/api/transfer" method="POST">
<input name="to" value="attacker">
<input name="amount" value="1000000">
</form>
<script>document.forms[0].submit()</script>Nếu alodev.vn dùng cookie auth thuần và không CSRF protect -> tiền chuyển thành công.
SameSite cookie - defense chính 2026
textSet-Cookie: session=...; SameSite=Lax; Secure; HttpOnly| SameSite | Behavior |
|---|---|
| Strict | Cookie KHÔNG gửi cross-site dù navigate. UX có vấn đề (link share không login) |
| Lax | Cookie gửi khi top-level GET navigation; KHÔNG gửi cross-site POST/iframe |
| None | Gửi mọi cross-site (cần Secure). Dùng cho third-party widget |
Lax là default mới của Chrome - chặn phần lớn kiểu CSRF cổ điển. Strict an toàn nhất nhưng UX kém.
Khi nào vẫn cần CSRF token?
- SameSite=None - third-party context bắt buộc (e.g. embedded widget)
- Defense-in-depth - proxy/browser cũ không respect SameSite
- Compliance - PCI, banking yêu cầu CSRF token explicit
- GET state-changing - không nên có nhưng nếu có, SameSite không bảo vệ
Double submit cookie pattern
javascriptimport crypto from 'node:crypto'
// Login: sinh CSRF token, set cookie (KHÔNG httpOnly để JS đọc được)
app.post('/auth/login', async (req, res) => {
// ... verify password
const csrfToken = crypto.randomBytes(32).toString('hex')
res.cookie('csrf', csrfToken, { sameSite: 'lax', secure: true })
res.cookie('session', sessionToken, { httpOnly: true, sameSite: 'lax', secure: true })
res.json({ ok: true })
})
// Middleware verify mọi state-changing request
function csrfProtect(req, res, next) {
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return next()
const cookieToken = req.cookies.csrf
const headerToken = req.headers['x-csrf-token']
if (!cookieToken || cookieToken !== headerToken) {
return res.status(403).json({ error: 'CSRF_TOKEN_MISMATCH' })
}
next()
}
app.use(csrfProtect)tsx// Frontend: đọc cookie, đặt vào header
function getCsrfToken() {
return document.cookie.split('; ').find(c => c.startsWith('csrf='))?.split('=')[1]
}
await fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCsrfToken() ?? '',
},
body: JSON.stringify({ to, amount }),
})Attacker không đọc được cookie victim (cross-origin) -> không thể gửi header đúng.
Synchronizer token (server-side state)
javascript// Render form với hidden CSRF token
app.get('/transfer', requireAuth, (req, res) => {
const token = crypto.randomBytes(32).toString('hex')
req.session.csrfToken = token
res.render('transfer', { csrfToken: token })
})
// Server-rendered HTML có:
// <input type="hidden" name="csrf" value="<%= csrfToken %>">
// Submit
app.post('/transfer', requireAuth, (req, res) => {
if (req.body.csrf !== req.session.csrfToken) {
return res.status(403).end()
}
// ... process transfer
})An toàn hơn double submit nhưng cần session storage (Redis). Dùng cho app server-rendered (Rails, Django, Laravel pattern).
Bearer token - không cần CSRF
javascript// SPA dùng Authorization header
fetch('/api/transfer', {
headers: { Authorization: `Bearer ${accessToken}` },
body: JSON.stringify(data),
})Token store trong memory (không cookie auto-attach) -> attacker site không có cách đính kèm. Đây là lý do nhiều SPA prefer Bearer + httpOnly refresh cookie.
Origin/Referer header check
javascript// Defense thêm - không đủ một mình
function originCheck(req, res, next) {
const origin = req.headers.origin || req.headers.referer
const url = new URL(origin || '', `https://${req.headers.host}`)
if (url.origin !== `https://${req.headers.host}`) {
return res.status(403).end()
}
next()
}Kết luận
CSRF token vẫn cần 2026 cho cookie auth - defense-in-depth với SameSite. Bearer token (JWT header) không cần. Double submit pattern đơn giản setup, đủ cho phần lớn ứng dụng. Tham khảo JWT authentication để hiểu khi nào chọn cookie vs header auth.





