PHP 如何防止 CSRF 跨站域请求伪造

CSRF 背景与介绍

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一。其他安全隐患,比如 SQL 脚本注入,跨站域脚本攻击等在近年来已经逐渐为众人熟知,很多网站也都针对他们进行了防御。然而,对于大多数人来说,CSRF 却依然是一个陌生的概念。即便是大名鼎鼎的 Gmail, 在 2007 年底也存在着 CSRF 漏洞,从而被黑客攻击而使 Gmail 的用户造成巨大的损失。



CSRF 攻击实例

CSRF 攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作。比如说,受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。


有个黑客 Mallory 自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。


接地气的案例

还有一个案例,很久以前,有个小生意,有些人提供这个的服务,可以提供所有访问过你网站人的QQ号,原理是这样的。QQ空间如果开通黄钻会员,可以记录QQ空间被拒绝访问人的列表。这些人就在客户网站中放入一个 img 标签,比如:

<img src="http://qzone.qq.com/666888">



如何防止 CSRF 攻击

要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。


token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 ,这样就把 token 以参数的形式加入请求了。


最佳实践

不使用 GET 方法执行敏感操作,使用 POST 方法,并在请求中添加 _csrf_token_。

1,服务端在收到路由请求时,生成一个随机数,在渲染请求页面时把随机数埋入页面表单,

2,服务端把该随机数保存到 session,当用户发送 GET 或者 POST 请求时带上_csrf_token_参数。

3,后台在接受到请求后,把用户请求提交的_csrf_token_和 session 中的 _csrf_token_ 做个比较,如果相等表示请求是合法的。


在 ThinkPHP 中

表单令牌 验证规则支持对表单的令牌验证,首先需要在你的表单里面增加下面隐藏域:

<input type="hidden" name="__token__" value="{$Request.token}" />

或者

{:token()}

然后在你的验证规则中,添加 token 验证规则即可,例如,如果使用的是验证器的话,可以改为:

protected $rule = [
  'name' => 'require|max:25|token',
  'email' => 'email',
];

如果你的令牌名称不是 __token__ ,则表单需要改为:

<input type="hidden" name="__hash__" value="{$Request.token.__hash__}" />

或者:

{:token('__hash__')}

验证器中需要改为:

protected $rule = [
  'name' => 'require|max:25|token:__hash__',
  'email' => 'email',
];



参考:

https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/

https://www.kancloud.cn/manual/thinkphp5/193918

https://www.kancloud.cn/manual/thinkphp5_1/354109

https://www.kancloud.cn/manual/thinkphp6_0/1037632

真诚赞赏,手留余香
赞赏
随机推荐
photoshop 数位板 两小时练习
TedPHP执行原生SQL查询的方法
可用于thinkphp5 r4第三方分页类
Chrome开发者工具的实用技巧(译)
Webpack的项目中,如何import导入绝对路径
2015-2016前端知识体系
npm warn package.json @1.0.0 no repository field
Android工程图片资源命名规则
PHP判断file框是否已选择文件
ffmpeg 推送摄像头拍摄内容到服务器