PHP 在反 CSRF 令牌上的进一步安全性
Posted
技术标签:
【中文标题】PHP 在反 CSRF 令牌上的进一步安全性【英文标题】:PHP further security on anti-CSRF token 【发布时间】:2012-10-31 02:09:57 【问题描述】:我正在学习如何使用反 CSRF 令牌来防止 CSRF。本质上,这个想法是:-
1) 生成一个令牌,例如 Md5 或 Sha1,然后将此值存储在会话变量中:-
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
2) 所有表单都在 POST 隐藏字段中包含此令牌值
<input type='hidden' name='token' value='$nonce_token' />
例如,用户在源代码中的样子:-
<input type='hidden' name='token' value='9ee66e4e63a06ee4b83a3edde4ecd587' />
3) 发送表单后,检查 POST 隐藏字段令牌值是否与会话值中存储的令牌匹配
if($_POST['token']==$_SESSION['token'])...ok...
然而,这个过程似乎有点缺陷,因为通过在隐藏的 POST 字段中包含令牌值,攻击者只需查看网站源代码即可查看令牌,然后将其包含在恶意生成的 POST 表单中,我的因此,一旦接收到令牌值,应用程序就会成功,因为发送的令牌值将与我的会话变量中的令牌值相匹配,因为我基本上将隐藏字段中的令牌值显示给攻击者。
因此,我的问题是最好的解决方法是什么,因为我的一些想法似乎仍然没有什么缺陷:-
1) 使用 _GET 代替,但这仍然存在 _POST 之类的缺陷
2) 在 x 分钟或每个请求后更改令牌值,但在返回浏览器时会导致可用性问题或在用户填写表单时失败,并且令牌值与更新的会话令牌值相比会变得过时,因为隐藏令牌值不会有在用户填写表格时更新。
3) 尝试加密隐藏的 POST 表单令牌值,然后在发送 POST 时解密,但加密/解密已经散列的值似乎很复杂,尤其是一种加密方式具有 MD5 等值?
任何想法都将不胜感激。
【问题讨论】:
【参考方案1】:您需要做的是使隐藏字段成为带有盐的会话 ID 的 MD5 或 SHA1 哈希。这样,您将提交的值与会话 ID 的哈希值加盐进行比较,如果它们匹配,则它是有效的。如果攻击者可以猜到令牌,那么他们已经窃取了会话 ID,并且由于登录已被劫持,因此再进行保护将毫无意义。真的就这么简单。这是每个 OWASP 关于如何防止 CSRF https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
的一些重要信息【讨论】:
谢谢,你说的越复杂,我在隐藏字段中显示的令牌(即添加 Sha1 而不是 + 盐)几乎使攻击者甚至无法考虑猜测 a 的令牌登录用户,因为每个用户都会生成不同的会话令牌,但如果生成令牌的算法是垃圾,攻击者更容易猜到? 所以,你的意思是在生成隐藏表单令牌值时类似于以下内容 - $token=sha1($session_id.$salt); ? 是的!在隐藏字段中插入该值。将服务器上提交的字段值与上面生成的相同值进行比较,如果相同,则表单是您的用户在服务器上提交的。如果攻击者可以使用相同的令牌创建另一个表单,则意味着他们窃取了会话 ID(cookie),甚至更糟的是盐(那么你手头有更大的安全问题)。如果他们有 cookie,他们无论如何都可以作为用户登录。会话 ID 将保持不变,因此如果您不想存储令牌,则无需存储令牌,因为它仍会为该会话 ID 生成相同的值。 顺便说一句,您也不需要更改盐,只需使其足够长并将其存储为常数即可。如果有人可以从散列会话 id 中猜出你有什么盐,那么世界各地的安全性就会崩溃,因为我们知道它 =oP 人们试图使 CSRF 保护过于复杂,但归结为防止某人使用伪造表单/请求让毫无戒心的用户使用他们的会话提交它。上面的例子将解决这个问题,它是防止它的最简单方法。请记住始终 POST 表单,这样您的令牌就不会在日志或 url 中公开。 感谢 Cryptic,非常好的信息,我会给你最好的答案,但必须给 Quentin,他的回答是第一位的,并消除了我最初的误解,但你的回答也有助于加深我的理解,非常感谢.【参考方案2】:让我们回顾一下攻击场景:
-
您的服务器位于
example.com
,并且您在表单中使用 CSRF 令牌。
每个 CSRF 令牌都是唯一的,特定于用户并且仅在一段时间内有效。
恶意第三方 Eve 诱骗您的用户 Alice 访问她的网站,试图发起 CSRF 攻击。
如果 Eve 只是诱骗 Alice 向您的服务器提交没有 CSRF 令牌的表单,您的服务器将拒绝它。
如果 Eve 在您的服务器上也有一个帐户并尝试获取 任何 令牌以与表单一起提交,这将失败,因为该令牌对 Alice 无效。
这留下了这种情况:使用 javascript,Eve 从您的服务器获取表单作为 Alice,然后将该表单提交回来,包括一个有效的令牌。 IE。 Eve 完全模仿 Alice 使用 Javascript 完成常规表单提交的整个过程。 Same Origin Policy 可以防止这种情况发生。 Eve 的 Javascript 将无法从您的服务器获取信息,Alice 的浏览器会阻止此操作,因为它违反了同源策略。
也就是说,假设浏览器中没有允许 Eve 绕过该策略的安全漏洞。这也意味着您需要防范 XSS,即防止 Eve 能够将她的脚本之一注入您的网站,因此您网站的常规访问者会将 Eve 的脚本作为您网站的一部分从同一来源运行。
为了自我推销,我刚刚实现了一个基于签名的 CSRF 令牌库,您可能想看看:Kunststube\CSRFP。在此期间,我还想征求同行评审和批评。
【讨论】:
感谢 deceze,这是一个很好的答案,也会尝试查看您提供的脚本。【参考方案3】:首先,您必须记住,您无法阻止黑客攻击您的应用程序,只有您可以让事情变得更难。
当您考虑 CSRF 攻击的主要目标是什么时,这个想法就很清楚了,CSRF 是一种欺骗受害者加载包含恶意请求的页面的攻击。从某种意义上说,它是恶意的,它继承了受害者的身份和特权以代表受害者执行不希望的功能,例如更改受害者的电子邮件地址、家庭住址或密码,或购买东西。 CSRF 攻击通常针对导致服务器状态更改的函数,但也可用于访问敏感数据。
如上所述,攻击者不会直接对您的网页进行攻击,他们需要桥接器,这就是他们需要受害者,因此他们可以使用受害者身份和权限来执行操作。
当你说:
但是,这个过程似乎有点缺陷,因为将令牌值包含在 隐藏 POST 字段攻击只需看网站源代码即可
这没有意义,因为攻击者不会攻击自己。
我希望这对您有帮助。
【讨论】:
这是有道理的 Mohammad 谢谢。由于隐藏帖子字段中的令牌对于登录的会话用户是唯一的。那么用户 bob 不会生成与用户 Alice 相同的会话令牌吗?那么,用户 Alice 要破解用户 Bobs 会话,Alice 需要猜测 Bobs 令牌,算法 + salt 越复杂,用户 Alice 猜测 Bobs 令牌的难度就越大? 没错,就是这样,现在我有建议让事情变得更难,不要将令牌添加到 URL(使用 $_GET 检查值),因为受害者可能会错误地将 URL 发送给攻击者(比如聊天,电子邮件等),那么每次用户执行某些操作时,您都必须更改该令牌,例如,如果他只是添加新帖子并且一切正常,则将会话中的令牌更改为新令牌。这将使事情变得更难,如果我们假设攻击者仍然是令牌一次他只能做一个动作(如果他还没有保持整个会话),然后令牌改变了。【参考方案4】:然而,这个过程似乎有点缺陷,因为通过将令牌值包含在隐藏的 POST 字段中,攻击者只需查看网站源代码即可
不,他们不能。
爱丽丝经营一个网站。 Bob 访问该网站。 Mallory 正在攻击 Bob 的帐户。
Bob 在访问 Alice 的网站时会获得一个 nonce 令牌。
如果 Mallory 访问了该站点,Mallory 会得到一个不同的随机数(因为 Mallory 会有不同的会话)。
如果 Mallory 生成了一个包含恶意数据的表单(在她的网站上)并欺骗 Bob 提交,那么 Mallory 放入表单中的 nonce 将与 Bob 会话中的 nonce 不匹配,并且提交将被拒绝。
【讨论】:
昆汀从来没有这样想过!因此,由于 Mallory 与 Bob 有不同的令牌,而且我在隐藏字段中显示的令牌越复杂(即添加 Sha1 而不是 + salt)几乎使 Mallory 甚至无法考虑猜测 bobs 会话的令牌。非常感谢 John 做了一个interesting comment :那么如果 Mallory 将 Alice 的网站嵌入到她自己的邪恶网站上,比如通过隐藏的 iframe,Mallory 可以通过 jQuery 读取令牌,并将其用于邪恶形式。服务器端令牌将与邪恶表单上的令牌匹配,因为嵌入站点会发出服务器请求... @olli — “Mallory 可以通过 jQuery 读取令牌” — 不,她不能。浏览器中的标准跨域安全功能使这成为不可能。 我差点就是这么想的……谢谢你的澄清以上是关于PHP 在反 CSRF 令牌上的进一步安全性的主要内容,如果未能解决你的问题,请参考以下文章
将 REST 服务的 CSRF 令牌作为 Http 响应标头包含在内是不是安全?