|
上周帮一个朋友看他的属于小电商后台,登录状态下点开一个第三方链接,漏洞账户里收货地址就被改了。防护方法他一脸懵,属于我告诉他这是漏洞典型的CSRF——跨站请求伪造。攻击者不需要偷你的防护方法密码,只要诱导你带着已登录的属于Cookie去访问一个恶意构造的请求,服务器就认了。漏洞 防护这件事,防护方法说复杂也复杂,属于说简单其实就是漏洞几个核心思路。我按自己踩过的防护方法坑和实际项目里的做法捋一遍。 Token校验:最主流的属于那道锁 表单里塞一个随机生成的token,服务端存一份,漏洞提交时比对。防护方法攻击者从别的站发请求,拿不到这个token,请求就废了。听起来简单,但细节容易翻车。 我见过有人把token放在URL参数里,结果被浏览器Referer头或者日志泄露出去。正确的做法是放在隐藏字段或者自定义请求头里。还有一点——token必须和用户会话绑定,不能全局用一个固定值。之前有个开源项目就是全局token,等于没设防。 Spring Security默认开启CSRF防护,用的就是这套机制。但如果你用前后端分离架构,记得把token通过Cookie传给前端,前端再放到请求头里带回来,比如X-CSRF-TOKEN。别偷懒关掉它,我见过太多人图省事在配置里写csrf().disable(),然后就没有然后了。 SameSite Cookie属性:浏览器帮你挡一刀 这算是近年最省事的防护手段。给Cookie加上SameSite=Lax或Strict,跨站请求就不会自动带上Cookie了。Lax模式下,顶级导航的GET请求还是会带,但POST、iframe、AJAX这些基本都被拦了。 Chrome从80版本开始默认把没设置SameSite的Cookie当作Lax处理,这一下子干掉了一大批CSRF攻击。但别完全依赖它——老浏览器不支持,而且有些业务场景确实需要跨站携带Cookie,比如嵌入第三方支付页面。所以SameSite是加分项,不是替代品。 验证Referer和Origin头 服务器检查请求来源,只放行自己域名下的请求。Origin头在跨站请求里一定会带,Referer虽然可能被隐私设置抹掉,但Origin相对可靠。 这招的局限在于:有些老浏览器不发Origin,有些代理会改Referer。而且如果网站有子域名,还得小心配置白名单,别把evil.yoursite.com也放进去了。我一般把它当辅助手段,配合Token一起用。 关键操作二次确认 改密码、转账、删账号这类敏感操作,强制要求输入原密码或者短信验证码。攻击者伪造的请求里没有这些信息,自然就过不了。用户体验上多一步,但安全边界清晰很多。 GitHub删仓库要你手动输入仓库名,就是这个逻辑。虽然麻烦,但确实管用。 自定义请求头 前后端分离的项目里,我习惯要求所有AJAX请求带上一个自定义头,比如X-Requested-With: XMLHttpRequest。浏览器对跨域的复杂请求会先发OPTIONS预检,攻击者的站点没法伪造这个头通过预检。简单粗暴,但有效。 不过这只是道矮墙,别当主力。有人觉得加了自定义头就万事大吉,结果被XSS配合着绕过去——XSS能直接发同源请求,自定义头随便加。 别忽略GET请求 很多人以为CSRF只针对POST,其实GET一样危险。早年有个路由器管理界面,点一个链接就能重启设备,就是因为GET请求执行了状态变更操作。RESTful规范里GET只做查询,但现实里违规操作太多了。我的原则很简单:任何会改变服务端状态的操作,一律用POST/PUT/DELETE,并且加上Token。 防护手段没有银弹。Token是底线,SameSite是趋势,Referer校验和二次确认是补充。具体项目里怎么组合,看架构和用户场景。但有一条——别因为麻烦就关掉框架自带的防护,那点麻烦比起被脱库,真的不算什么。 |