【问题标题】:How does Symfony2 CRSF protection work?Symfony2 CRSF 保护如何工作?
【发布时间】:2014-11-04 05:00:20
【问题描述】:

我正在尝试测试 Symfony2 完成的 CRSF 保护系统,非常感谢他们。
我的security.yml模板:(我修改了默认的。)

security:

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
            pattern:  ^/demo/secured/login$
            security: false
        secured_area:
            pattern:    ^/demo/secured/
            form_login:
                check_path: _security_check
                login_path: _demo_login
                csrf_provider: form.csrf_provider
            logout:
                path:   _demo_logout
                target: _demo
            #anonymous: ~
            #http_basic:
            #    realm: "Secured Demo Area"

    access_control:
        #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }

以我的形式:

<input type="hidden" name="_csrf_token" value="{{ csrf_token("authenticate") }}">

这会产生如下内容:

<input type="hidden" name="_csrf_token" value="cKzXBHRDX_sHuT4qt9TAJIwgRvtRMtPnFDtitrSZDuw">

我不知道 symfony 如何使用令牌处理验证,但在提交登录之前,我使用 firebug 手动更改了令牌的值,如下所示:

<input type="hidden" name="_csrf_token" value="MODIFIEDcKzXBHRDX_sHuT4qt9TAJIwgRvtRMtPnFDtitrSZDuw">

当我提交登录时,我会登录。这意味着令牌没有影响。 我哪里错了?


狙击狩猎

  1. Symfony 版本是 2.5.2
  2. 当我手动将会话变量“logged”设置为 true 时,系统将我登录。这发生在从数据库读取并比较密码之后。
  3. 表单HTML!

    <form id="Loginform" onsubmit="OrganicLogin();return false;">
         <input type="hidden" name="_csrf_token" value="{{ csrf_token("authenticate") }}">
        <div id="Loginresponse" style="display:none;"></div>
            <div class="form-group" style="overflow:hidden;">
            <label style="margin-top:10px;" for="inputUsername" class="col-lg-2 control-label">Username</label>
            <div class="col-lg-10">
            <input type="text" class="form-control" id="inputUsername" placeholder="Username" style="width:215px;float:right;">
            </div>
            </div>
            <div class="form-group" style="overflow:hidden;" >
            <label style="margin-top:10px;" for="inputPassword" class="col-lg-2 control-label">Password</label>
            <div class="col-lg-10">
            <input type="password" class="form-control" id="inputPassword" placeholder="Password" style="width:215px;float:right;">
            </div>
            </div>
            <div class="form-group" style="overflow:hidden;text-align:center;" >
            <button type="submit" class="btn btn-primary btn-block" id="submitButton">Access</button>
            </div></form>
    
  4. 是的!我做了

  5. 其实我一直在争论的是,我以原生方式完成了登录过程,表单,用 JS 读取数据,向控制器发送 POST 请求,控制器检查输入并设置会话。

  6. 不,全部手工完成

  7. 其实这是我第一次使用security.yml,我只是删除了一些我认为对这个线程没有用的部分

  8. 没有..

【问题讨论】:

  • 我的登录网址是 /login
  • 你可以通过 cURL 和 POST 方法测试 ist(只是不要设置_csrf_token 字段)并打印输出。
  • 我需要在控制器中写什么吗?
  • 不使用外部脚本(如果您的意思是 cURL 测试)。
  • 我不是说 cUrl 测试,您的第一条评论是指仅设置 csrf_token 可以绕过保护?如果你知道请告诉我 symfony 如何比较 $_POST['csrf_token'] 和其他元素..我很困惑..

标签: security symfony csrf csrf-protection


【解决方案1】:

它的工作方式是 Symfony 生成一个 CSRF 令牌,它会自动将其插入到表单中。它将这个令牌存储在当前会话中。提交表单时,它将提交的令牌与存储在会话中的值进行比较。关于您的具体情况,听起来 CSRF 实际上并未启用,这可能与启用 CSRF 的安全区域防火墙和未启用 CSRF 的登录防火墙之间未共享安全上下文有关。

尝试在您的 security.yml 中删除此位:

login:
    pattern:  ^/demo/secured/login$
    security: false

相反,将其移动到secured_area 上下文中并使用访问控制来授予访问权限:

...

form_login:
    check_path: _security_check
    login_path: login
...

access_control:
    - { path: ^/demo/secured/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }

或者,您可以尝试为您的登录防火墙添加context: secured_area。根据我的经验,如果登录防火墙不在与安全区域相同的上下文中,您将无法完全从登录控制器中访问安全上下文。

【讨论】:

    【解决方案2】:

    我有点猜测您更改的令牌没有发布。掷骰子:

    namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
    
    class DefaultCsrfProvider implements CsrfProviderInterface
    {
        public function isCsrfTokenValid($intention, $token)
        {
            die('csrf token ' . $intention . ' ' . $token);
            return $token === $this->generateCsrfToken($intention);
        }
    

    如果达到骰子,那么您就知道您的配置没问题,当然您可以看到实际发布的令牌。

    不用说,你也应该清除缓存。

    ================================================ ========

    更新 1 - 在多次 cmet 之后,我们确定没有调用 die()。进展。

    不幸的是,我们仍然需要准确验证发布者是如何配置他们的系统的。

    下一步 - 通过 firebug 在不调整 csrf 令牌的情况下登录并验证是否到达 die 语句。

    以一种或另一种方式报告。

    不用说(但无论如何我都会说),请确保您在尝试重新登录之前注销。

    ================================================ =========

    更新 2 - 即使正常登录也无法到达 die 语句。

    所以现在我最喜欢的部分来了。狙击狩猎。基本上,我在阅读问题时做了一些假设。需要通过问一些基本问题来确定哪些假设是不正确的。

    1. 您使用的是哪个版本的 Symfony 2。我假设至少 S2.1。

    2. 您如何知道系统已将您登录?您是否正在使用调试工具栏,它是否显示您已通过身份验证?当您尝试使用错误密码登录时会发生什么?

    3. 使用浏览器的查看源代码功能并将生成的表单复制到您的问题中。特别是我想看到 action 属性,但我也想看到 input 元素。

    4. 您实际上是否将 die 语句添加到 vendor/symfony/symfony/src/Symfony/Component/Form/Csrf/CsrfProvider/DefaultCsrfProvider.php 中?文件编辑后保存了吗?

    5. 您实际上是在使用标准的 form_login 流程,对吗?您没有任何代码,例如检查用户密码?

    6. 您是否在使用任何其他捆绑包,例如 FOSUserBundle?

    7. 您问题中的 security.yml 文件真的是您的实际文件吗?复制后没有“清理”?

    8. 您是否已将您的应用程序签入 github?如果是这样,你能提供一个链接吗?查看整个应用程序可能是解决此问题的最快方法。

    现在应该足够了。用你的答案更新你的问题。

    ================================================ ============================

    更新 3 - 情节变厚

    当我输入上述问题时,我们发现基本登录系统本身没有正确配置。调试工具栏指示用户未通过身份验证。更多进步!正如经常发生的那样,症状掩盖了实际问题。

    安全系统可以说是 Symfony 2 中典型开发人员需要与之交互的最复杂的组件。配置时容易混淆,故障排除困难。一个小小的错字就能把事情搞砸。对于开发人员来说,对如何实现安全性有一个有效的理解也非常重要。当然,除非你是像 Target 或 Home Depot 这样的大公司。

    我的建议是使用 composer 创建一个全新的 Symfony 2 项目。然后一步步通过http://symfony.com/doc/current/book/security.html,配置安全系统。让这成为理解安全性的一种参考应用程序。

    到该过程结束时,我怀疑您会发现问题并可以将解决方案应用于您现有的应用程序。作为奖励,您将获得一些可以参考以解决未来问题的内容。

    ================================================ ====================

    更新 4 - 令人兴奋的结论

    所以现在我们发现正在使用一个自定义且幼稚的登录系统。

    我仍然建议从一个新项目重新开始,并以 Symfony 2 的方式运行。之后,如果您真的愿意,您可以调整登录表单以使用 javascript。

    如果您真的很想使用自己的系统,请从这里开始:Manual authenticate user

    但你会无缘无故放弃 Symfony 的主要优势之一。

    【讨论】:

    • 这实际上是我最初的问题,我需要在我的控制器中添加一些东西来使保护工作吗?
    • 不。看起来您关注的是:symfony.com/doc/current/cookbook/security/…。这就是你需要做的。在检查登录过程中,控制器永远不会受到打击。全部由听众完成。如果达到 die 语句,那么您已经正确配置了它。如果没有,那么您还有其他事情要做。
    • 如果您真的想要用户身份验证的丑陋细节,请查看:symfony.com/doc/current/cookbook/security/…。但这不适合胆小的人。相信我,我最终会调用 isCsrfTokenValid()。
    • 谢谢!那很有意思。你说了一些我不知道的棘手问题。 “在检查登录过程中,控制器永远不会被击中。全部由侦听器完成”我的登录过程在控制器内!我需要改变什么吗?
    • 您的 login_path 使用控制器来呈现登录表单。但是当您发布表单时,它会转到 check_path。该 get 被侦听器拾取并执行实际的用户身份验证。再一次,如果你按照这本书去做,那么你不需要做任何其他事情。
    猜你喜欢
    • 1970-01-01
    • 2018-08-09
    • 2017-04-03
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 2017-04-15
    • 2016-12-01
    • 2014-07-27
    相关资源
    最近更新 更多