【问题标题】:Do I need a CSRF token for jQuery .ajax()?我需要 jQuery .ajax() 的 CSRF 令牌吗?
【发布时间】:2012-02-23 18:49:04
【问题描述】:

所以我有一个基本的 .ajax() POST 方法到 PHP 文件。

我需要什么安全措施?

周围的一些帖子提到使用您通过 AJAX 发送并在 PHP 文件中验证的隐藏 MD5 输入字段。这是一个足够好的方法吗?

【问题讨论】:

    标签: jquery ajax security post csrf


    【解决方案1】:

    来自 CSRF 的风险是外部站点可能会向您的站点发送数据,并且用户浏览器会自动发送身份验证 cookie。

    您需要某种方式让接收操作(您的 $.ajax() 方法正在向其发送 POST 数据)能够检查请求是否来自您网站上的另一个页面,而不是来自外部网站。

    有几种方法可以做到这一点,但推荐的方法是在请求中添加一个令牌,您可以检查它并且黑客无法访问。

    最简单的:

    • 在登录时创建一个长的随机字符串令牌并将其保存在用户面前。
    • 向包含令牌的$.ajax() 请求添加一个参数。
    • 根据请求检查令牌是否与您为用户保存的令牌相匹配。
    • 如果令牌不匹配,您就有了 CSRF hack。

    黑客无法访问您的数据库,也无法真正读取您发送给用户的页面(除非他们受到 XSS 攻击,但这是另一个问题),因此无法欺骗令牌。

    对于令牌而言,重要的是您可以预测(并验证)它而黑客不能

    因此,最容易生成长而随机的内容并将其存储在数据库中,但您可以构建一些加密的内容。不过,我不会只对用户名进行 MD5 - 如果 CSRF 攻击者弄清楚如何生成您的令牌,您将被黑客入侵。

    另一种方法是将令牌存储在 cookie(而不是您的数据库)中,因为攻击者无法读取或更改您的 cookie,只会导致它们被重新发送。那么你就是 HTTP POST 数据中的令牌匹配 cookie 中的令牌。

    您可以使这些更复杂,例如每次成功使用时都会更改的令牌(防止重新提交)或特定于用户和操作的令牌,但这是基本模式。

    【讨论】:

    • 尽管Same-origin policy 阻止了此类行为,但用户如何从其他网站发布 AJAX 请求?
    • @Songo 不幸的是,并非所有浏览器都支持。许多代理剥离标题并打破它。最后,您可以从源外部 POST,因此即使您打算对它进行 AJAX,但这并不意味着攻击者会这样做。基本上你应该有一个同源策略,但由于它依赖于表现良好的浏览器,你不应该依赖它。使用 CSRF 令牌可以为您提供一些可以验证的东西,即使同源被规避。
    • @arleslie 只是为了清楚起见 - 将 cookie 与您的数据库进行比较根本不会提供额外的安全性(您的身份验证 cookie 已经这样做了)。重要的是将 POST 数据中的令牌 either 与数据库中的令牌或 cookie 中的令牌进行比较。攻击者可以重新发送 cookie 但无法读取它,因此他们的恶意 POST 数据无法包含正确的值。
    • @arleslie 在 CSRF 攻击中黑客看不到您的 cookie,他们无法解析您的请求或读取您发送或接收的内容。如果他们入侵了您的机器,那么您无能为力,但 CSRF 攻击要简单得多:攻击者并未入侵您的机器,但可以诱骗您将其有效负载提交到您已经登录的站点。他们无法读取您的 cookie,但可以将其连同其内容一起重新发送。您可以通过确保 POST 的内容(不仅仅是 cookie)中包含可验证的数据来阻止这种攻击。
    • @arleslie 一个典型的例子是我(作为黑客)制作并让您访问的页面 - 一旦在该页面上我向 Facebook 提交了一个帖子。我对你的 Facebook cookie 甚至你的名字一无所知,但该 POST 将发送你的 FB auth cookie 以及我的指令,以便在你的墙上点赞和分享我的黑客页面(这实际上发生在 Facebook 上)。在 CSRF 攻击中,我绝不会拦截您发往或来自 Facebook 的请求 - 我只是依靠您的浏览器再次发送它。
    【解决方案2】:

    就请求伪造而言,客户端如何发送请求并不重要,重要的是它如何接收。与任何其他类型的帖子相同的 CSRF 规则适用于 ajax 帖子。

    我建议阅读CSRF prevention cheat sheet。使用每个用户的秘密令牌是最常见的保护形式。

    【讨论】:

    • 相当常见的还有 per-request 一次性令牌,为特定用户获取并在首次使用后禁用。
    • @Tadeck 这种方法对于防止重复提交比 CSRF 更有用。
    • the source you referenced 中所述,一次性令牌是在高风险功能中使用的非常强大的安全性。这与“对防止重复提交比 CSRF 更有用”相反,它是保护您的应用程序免受 CSRF 攻击的更严格的方法。
    • @Tadeck 我在那个 wiki 上有写权限,我的一些话在那个文档中。试图猜测一个巨大的令牌是不现实的攻击。
    【解决方案3】:

    严格来说,不需要令牌,但您仍应保护任何更改状态的函数免受 CSRF 影响。

    CRSF 绝对是一种风险,即使请求是通过 AJAX 发出的。这是因为 AJAX 请求可以跨域传递——同源策略只防止读取,而不是写入。而且传统表单可能能够发送与您的 AJAX 完全相同的 POST 请求,而您当前的服务器端代码可能无法检测到这一点。

    让您的服务器端代码检测请求是否来自您自己的站点的一种简单方法是添加与 AJAX 请求一起发送的标头。重要的是您的服务器端代码检查此标头的存在。不一定需要随机令牌。

    之所以有效,是因为:

    • HTML 表单不能由攻击者添加自定义标题。
    • 如果未启用 CORS,则无法跨域传递自定义标头。

    为了防御网络上的任何未来发展,同时实现一个随机令牌可能是个好主意。这需要以某种方式与当前用户会话相关联。如果没有实现令牌,它目前是不可利用的,但在网络漫长而曲折的历史中,缺乏令牌可能会被 Flash 和其他浏览器插件利用。在一个完美的世界中,HTML5 和生活标准应该意味着像这样的插件已经成为过去,但是,谁知道即将发生什么,所以为了增加深度防御和未来的证明,令牌也是推荐。

    更多信息:What's the point of the X-Requested-With header?

    【讨论】:

      【解决方案4】:

      这是一个可以用 django 尝试的简单演示:

      在 HTML 页面上

      {%block content%}
      <form id="userForm">
      {%csrf_token%}
        <input type="text" id="username" placeholder="User Name">
        <input type="password" id="password" placeholder="Password">
      </form>
      {%endblock%}
      

      Java 脚本代码

      %(document).on('submit','#userForm',function(e){
         e.preventDefault();
      
       $.ajax({
      
          type = 'POST',
      
          url:'path/to/url',
      
          data:{
           username:$('#username').val(),
           password:$('#password').val(),
           csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken').val()
          },
      
         success:function(data){
             alert('Successfull');
         }
        });
      
      });
      

      【讨论】:

        猜你喜欢
        • 2023-03-08
        • 2021-09-27
        • 2016-02-11
        • 2011-06-17
        • 2019-12-30
        • 2015-05-14
        • 2018-12-11
        • 2011-06-13
        • 2019-04-01
        相关资源
        最近更新 更多