验证码能防 CSRF 吗?我这样用对不对?

一汉霖 阅读 4

最近在给登录接口加 CSRF 防护,听说加验证码可以防,但我试了下感觉不太对劲。前端提交表单时带上了用户输入的验证码,后端也校验了,但好像还是可能被 CSRF 攻击?是不是我理解错了?

我现在是这么做的:用户输入用户名、密码和验证码,一起 POST 到 /login,后端验证验证码是否正确。但 CSRF 攻击者根本看不到验证码图片,应该没法伪造请求吧?可同事说这不保险……

fetch('/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'test',
    password: '123456',
    captcha: userInputCaptcha // 用户手动输入的验证码
  })
})
我来解答 赞 9 收藏
二维码
手机扫码查看
1 条解答
Mr-东宁
Mr-东宁 Lv1
验证码确实能防CSRF,但你的用法有点问题,而且你同事的担心是有道理的。

先说为什么能防:CSRF攻击的核心是攻击者诱导用户访问一个恶意页面,让浏览器自动发请求到你的服务器。由于同源策略,攻击者的页面是看不到你的验证码图片的,所以他没法把正确的验证码塞到请求里。这样服务器校验验证码时就会失败,攻击就被挡住了。

但问题在于你只保护了登录接口。CSRF攻击通常发生在用户登录之后,比如修改密码、转账、删除数据这些敏感操作。你如果只在登录页用验证码,那其他接口怎么办?总不能每个操作都让用户输验证码吧,用户会疯的。

另外,用验证码防CSRF还有个隐患:验证码必须是一次性的、绑定到用户session的。如果你实现时有什么漏洞(比如验证码存在cookie里、可以重复使用),那防护就形同虚设了。

更标准的做法是用CSRF Token。流程是这样的:用户访问页面时,服务器生成一个随机token存在session里,同时传给前端埋到表单或请求头里。提交请求时带上这个token,服务器校验token和session里的是否匹配。由于攻击者的页面拿不到这个token(同样受同源策略限制),所以伪造的请求会被拒绝。

简单说:验证码能防CSRF,但不是最佳方案。建议登录用验证码防暴力破解,其他敏感操作用CSRF Token。如果你非要只用验证码,那就确保每个请求都生成新验证码、绑定session、校验一次就失效。
点赞
2026-03-12 09:01