Session绑定后怎么还是被CSRF攻击了?我的实现有问题吗?
我在做用户登录时把sessionID存到cookie里,并在服务端把session和用户ID做了绑定。但测试时发现,攻击页面通过已登录的浏览器发起请求,服务端居然能拿到正确的用户信息。我试过设置cookie的SameSite=Strict和HttpOnly,但好像没用…
服务端中间件代码是这样校验的:if (session.userId === req.user.id),但好像没检查Referer。是不是应该同时验证Referer和Origin头?或者Session绑定的时机不对?
// 服务端中间件片段
app.use((req, res, next) => {
const sessionUserId = req.session.userId;
const currentUserId = req.user.id;
if (sessionUserId !== currentUserId) {
return res.status(401).send('Session invalid');
}
next();
});
现在遇到的情况是,当用户访问恶意网站时,攻击页面发送的POST请求居然能通过验证,服务端没有检测到Referer异常。是不是需要在绑定session时强制关联Referer信息?或者我的验证逻辑完全错了?
首先,SameSite=Strict确实能防一部分CSRF,但它并不是所有浏览器都完全支持,尤其是老版本浏览器可能直接忽略这个属性。所以不能完全依赖它。
其次,Referer和Origin的校验是有必要的,但要注意它们并不是万能的。有些情况下,比如HTTPS跳HTTP,Referer可能会被浏览器丢弃。你可以试试在中间件里加个逻辑,判断请求头里的Referer或Origin是否匹配你的站点域名。代码可以这样写:
另外,更推荐的做法是使用CSRF Token机制。简单来说,服务端生成一个随机的CSRF Token,存到用户的session里,并在页面渲染时把这个Token嵌入到表单或者HTTP头中。每次收到POST请求时,服务端再校验这个Token是否匹配。这样即使攻击者伪造了请求,他也拿不到正确的Token。以下是一个简单的实现思路:
1. 用户访问页面时,服务端生成一个CSRF Token,存到session里,同时把它渲染到前端页面。
2. 前端发起POST请求时,把Token放在请求头或者表单参数里。
3. 服务端收到请求后,对比请求里的Token和session里的Token是否一致。
示例代码如下:
总结一下,Referer和Origin的校验可以加,但不要完全依赖它们。最好的办法是引入CSRF Token机制,这才是防御CSRF攻击的标准做法。希望这些建议能帮你解决问题!
你现在的校验逻辑是看session.userId和req.user.id是否一致,这没问题,但它并不能防御CSRF。因为你并没有验证请求的来源是否可信。
### 为什么SameSite=Strict没生效?
你设置的是Cookie的SameSite=Strict,这个属性确实能防CSRF,但它的限制是:**只在用户主动跳转时才允许发送Cookie**,例如点击链接或直接在浏览器地址栏输入。但如果你的攻击页面是通过JavaScript发起POST请求,只要不是导航行为,SameSite=Strict不会阻止请求带上Cookie(尤其是当你用的是
fetch或XMLHttpRequest)。如果你希望限制AJAX请求来源,那就不能只靠SameSite。
---
### 为什么没验证Origin和Referer就会出问题?
你已经意识到这个问题了,没错。**CSRF防御的核心就在于验证请求来源**,也就是检查:
- 请求头中有没有
Origin- 请求头中有没有
Referer- 它们的值是否在你信任的白名单中
你当前中间件没有做这部分校验,所以攻击页面发来的请求,只要用户已登录,就能成功拿到用户信息。
---
### 建议改进方式
1. **中间件里加来源检查**
你可以这样改:
2. **或者使用CSRF Token机制**
更标准的做法是服务端生成一个随机Token,前端每次请求都要带这个Token(比如放在Header里),服务端验证它的有效性。这比单纯校验Origin更安全。
你可以用像 [csurf](https://www.npmjs.com/package/csurf) 这样的中间件:
然后前端需要从Cookie或响应头里拿到这个Token,并在请求Header中带上它。
---
### 小结一下
- Session绑定用户ID是基本操作,但不能防CSRF
- SameSite=Strict有局限,AJAX请求仍可能带Cookie
- 要防御CSRF,必须验证来源(Origin/Referer),或者使用CSRF Token
- 现在推荐用CSRF Token机制,比手动验证来源更标准、更安全
建议你改成使用CSRF Token,这样能覆盖各种情况,也符合主流框架的做法。