我的 CSRF 保护方法安全吗?

Posted

技术标签:

【中文标题】我的 CSRF 保护方法安全吗?【英文标题】:Is my CSRF protection method secure? 【发布时间】:2018-03-09 00:44:11 【问题描述】:

我一直在使用 php 进行自己的 CSRF 保护。根据我所读到的内容,我决定使用 cookie 来实现我的保护,但对于我的方法是否能够抵御 CSRF 攻击感到有点困惑。

所以我的方法如下:

    用户发送登录请求

    服务器检查是否设置了 CSRF 令牌,如果未设置,则创建一个并将其存储在其 Session 中并使用该令牌创建一个 Cookie

    通过检查是否在 POST 请求中来验证 CSRF 令牌,如果不在则检查 $_COOKIE 中的令牌

    如果令牌无效则发回消息...

我决定使用 cookie 来存储令牌,因为这适用于 Ajax 请求,而且我不必在每次使用 Ajax POST 时都包含它。

我感到困惑的是攻击者不能只是提出请求吗? POST 或 GET,因为 cookie 在那里,它只是随请求一起发送,因此是一个有效的请求,因为令牌每次都与浏览器一起发送?

【问题讨论】:

CodeReview 会更适合你,我相信。 浏览器将阻止跨站点请求,因此任何在 cookie 中具有有效 CSRF 令牌的请求通常来自浏览您自己的域的客户端(除非您在某些路由中启用了 CORS,在这种情况下您需要小心)。 ***.com/a/20518324/487813 是一本很好的读物 @apokryfos no ,浏览器一般不会阻止跨站请求。它们会阻止您希望取回数据的请求。 XMLHttpRequest 使用 CORS。但是使用 javascript 提交的隐藏 <form> 仍然可以向外部域发送 POST 请求。并且此类请求将包括用户的所有 cookie。将 CSRF 存储在 Cookie 中就像没有 CSRF 令牌一样。 @apokryfos 涵盖此内容的相关部分是 [...]Simple cross-origin requests generated outside this specification (such as cross-origin form submissions using GET or POST or cross-origin GET requests resulting from script elements) typically include user credentials, so resources conforming to this specification must always be prepared to expect simple cross-origin requests with credentials.[...] (4 Security Considerations) 【参考方案1】:

cookie 不应包含 CSRF 令牌。只有客户端会话存储应该包含它。而且你不应该反对 cookie 中的 CSRF。

如果您检查与 cookie 一起发送的 CSRF,您将绕过 CSRF 背后的想法。

一个简单的攻击场景是外国网站上的隐藏表单:

<form method="method" action="http://site-to-gain-access-to.tld/someactionurl">
  <!-- some form data -->
</form>

并且这个隐藏的表单将在没有用户干预的情况下使用 javascript 执行。如果用户在site-to-gain-access-to.tld 站点上登录并且如果没有激活 CSRF 保护,那么就像用户本身会触发该操作一样,因为用户的会话 cookie 将与该请求一起发送。因此服务器会假设是用户触发了该请求。

如果您现在将 CSRF 令牌放入您的 cookie 中,您将遇到与会话相同的问题。

CSRF 令牌必须始终仅作为请求正文或 url 的一部分发送,而不是作为 cookie。

所以突出显示的部分:

服务器检查是否设置了 CSRF 令牌,如果没有则创建一个并将其存储在他们的 Session 中并创建一个 带有令牌的 Cookie

通过检查是否在 POST 请求中来验证 CSRF 令牌,如果不在则检查 $_COOKIE 中的令牌

会破坏 CSRF 保护。 CSRF 是作为明文存储在 cookie 中还是以服务器端加密的形式存储并不重要。

【讨论】:

对,好吧,我想这对我来说很清楚。如果我是正确的,那么仅将 CSRF 令牌作为隐藏输入包含在我要发送的表单中,然后将其与服务器上会话中的令牌进行比较就足以防止 CSRF 攻击? 是的,您应该只将其作为隐藏输入包含在表单中,并将其与服务器端会话存储进行比较。这是使您的网站安全的一部分。您肯定需要确保您的站点再次保存 XSS 攻击,否则恶意脚本可能能够访问该 CSRF 令牌。所以 session id 确认请求是由用户的浏览器完成的,CSRF 确认请求是由用户完成的。 好吧,这是有道理的。但是对于 Ajax 请求,包含位于页面上 div 中的 CSRF 令牌是否仍然安全,因为我的一些 Ajax 请求不是来自正在发布的表单? @Erdss4 是的,您可以将 CSRF 令牌与 ajax 请求一起传递,作为请求正文中的属性,或作为带有 x- 标头的标头,例如 X-CSRF-Token。个人会使用 x- 标头,因为它不会干扰您的请求数据。 @Erdss4 您将 data attribute 添加到 html 元素 (Using data attributes)。这些可以在现代浏览器中使用nodeElement.dataset 直接访问,也可以在库中访问,例如:jQuery.data()【参考方案2】:

只要“用户发送登录请求”是指登录页面的初始GET请求,这是正确的。 CSRF 应该在 GET 请求中生成和存储,并在每次 POST 期间进行验证。

我感到困惑的是攻击者不能只是提出请求吗? POST 或 GET,因为 cookie 在那里,它只是随请求一起发送,因此是一个有效的请求,因为令牌每次都与浏览器一起发送?

是的。 CSRF 令牌不是秘密的。他们只是确认只有在发出预期的 GET 请求后才发出 POST。这意味着某人只有在他们首先请求表单时才能提交表单。它可以防止不良站点通过 POST 将用户发送到您的站点。它不会阻止攻击者发出 GET 请求、获取令牌和发出有效的 POST。

来自OWASP:

借助一些社会工程学的帮助(例如通过电子邮件或聊天发送链接),攻击者可能会诱骗 Web 应用程序的用户执行攻击者选择的操作。如果受害者是普通用户,成功的 CSRF 攻击可以强制用户执行状态更改请求。

【讨论】:

因此,如果我只是将我的令牌作为隐藏输入包含在每个表单中,并使用存储在 SESSION 变量中的令牌来验证令牌,这还不够吗?您是说令牌也应该是表单操作中 GET 请求的一部分吗? 在隐藏的输入元素中包含标记就足够了(只要您的站点不更改 GET 请求的数据)。我在解释令牌是在 GET 请求期间生成的(用表单检索页面)。您可以使用 cookie 或隐藏的输入元素,然后通过 POST 再次提交令牌。 那么您是否可以忽略在表单中放置隐藏的输入,而只依赖每次发送的 cookie? Cookie 需要一些额外的注意事项。我看到它们列在其他人的 cmets 和帖子中。还可以从我提供的 OWASP 链接中阅读相关页面。

以上是关于我的 CSRF 保护方法安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

没有会话或数据库的安全 CSRF 保护?

Codeigniter CSRF 问题

已经实施 csrf 保护时使用会话 id 的目的是啥?

6种方法绕过CSRF保护

春季安全 CSRF CORS

JWT 在未经身份验证的端点上进行 CSRF 保护?