如何在 PHP 中有效地防止跨站请求伪造 (CSRF)
Posted
技术标签:
【中文标题】如何在 PHP 中有效地防止跨站请求伪造 (CSRF)【英文标题】:How to prevent Cross-site request forgery (CSRF) effectively in PHP 【发布时间】:2016-02-19 22:52:04 【问题描述】:我试图通过以下方式阻止CSRF 中的php:
$_SESSION['token']
在每个页面的开头生成。我已经知道使用$_COOKIES
是完全错误的,因为它们是针对每个请求自动发送的。
在每个<form>
中,附加以下输入:<input type="hidden" name="t" value="<?php echo '$_SESSION['token']; ?>">
。
$_SESSION['token'];
已通过 $_POST['t']
进行验证
现在我有几个小问题:
这是防止 CSRF 的好方法吗?如果不是,请解释。 当打开另一个设置相同$_SESSION
变量的页面时,前一个(仍然打开的)页面会失效,如何防止这种情况发生?
对于表单,这个方法是明确的,但是如何处理正常的链接呢?是否也需要将令牌附加到每个链接?
非常感谢您。
【问题讨论】:
有一些方法可以进一步强化这一点(例如,将令牌绑定到单个表单,这样它们就不能在不同的上下文中重复使用,这就是我们的 anti-csrf 库所做的)。但你所描述的是大多数人都会做的事情。 感谢@ScottArciszewski 提供的链接和建议,我希望自己掌控一切,而不是依赖第三方库,但进一步限制它是一个非常好的主意. 【参考方案1】:当黑客试图从经过身份验证的用户发送虚假请求时,就会发生 CSRF 攻击。通常会发生这种攻击 在网上商店或银行。
在php中防止csrf攻击我们可以: 1 = 创建检查登录功能: 如果设置了登录会话,则为 true 好的,如果不是 false 并返回登录页面。 2 = 创建一个随机的 makeToken 哈希函数:base64_encode(md5(microtime())) 并将其保存到会话中,并创建一个输入隐藏类型,其名称为 Token 函数和名称为 Token 的函数。 3 = 创建一个 checkTocken 函数并检查它,如果它等于 makeToken 函数,然后使用 unset 函数取消设置会话,并创建一个新的。
【讨论】:
【参考方案2】:这是预防 CSRF 的好方法吗?
是的。这样做是为了强制客户端在对表单处理程序执行 POST 之前对表单执行 GET。这可以防止 CSRF 在现代浏览器中,因为浏览器会阻止客户端 javascript 向外部域发出 XHR GET 请求,因此第 3 方无法在其网站上模仿您的表单并成功获取有效令牌提交。
当打开另一个设置相同 $_SESSION 变量的页面时,前一个(仍然打开的)页面变得无效,如何防止这种情况发生?
一次允许多个令牌有效,在会话中保留一组有效令牌。或者,根本不存储令牌,而是使用令牌签名方案。我已经涉足并解释了here。备选方案 2:在整个会话中只使用一个令牌,而不会使令牌失效。 (向 cmets 中的@SilverlightFox 致敬)
对于表单这个方法是明确的,但是如何处理正常的链接呢?是否也需要将令牌附加到每个链接?
没有。你只需要保护 POST 请求,因为大概只有 POST 请求可以改变敏感数据(眨眼眨眼轻推,你坚持 REST 约定,对吧?!)并且 XHR GET 请求已经被浏览器阻止 -一边。
【讨论】:
感谢您的链接,是的,我当然会检查有效性,让我们更新我的问题以明确这一点。 什么是处理多个令牌的便捷方法,我可以使用页面 url 例如(假设用户没有两次打开同一个页面..) 我会在会话中保留一组$token => $timestamp
值;在每个表单提交array_filter
那个数组上丢弃过期的token,然后用isset($_SESSION['tokens'][$_POST['token']])
检查token是否有效。
我不明白为什么在 POST 之前进行 GET 是必要的?令牌验证可以在任何数据处理之前完全在服务器端完成。还是我误解了您关于提交验证的建议?
@Glapa 客户端需要发出一个 GET 请求以加载包含有效令牌的表单,该令牌需要包含在他们的 POST 请求中。通过扩展,OP 应该注意不要通过 POST 泄露有效的令牌。以上是关于如何在 PHP 中有效地防止跨站请求伪造 (CSRF)的主要内容,如果未能解决你的问题,请参考以下文章
request.getHeader("Origin") 如何防止跨站请求伪造攻击?