【问题标题】:How can I protect users from cross-site request forgeries using the Zend Framework?如何使用 Zend 框架保护用户免受跨站点请求伪造?
【发布时间】:2010-12-10 05:02:40
【问题描述】:

我的程序使用 Zend Framework,我想使用 Zend_Form_Element_Hash 保护用户免受 CSRF 的影响。但这似乎不起作用。

例如,我的注销表单代码是

    $exithash = new Zend_Form_Element_Hash('hihacker', array('salt' => 'exitsalt'));
    $this->addElement($exithash);

在我的控制器 Auth 插件中我这样做

    $exitForm = new R00_Form_Exit();

    if ($exitForm->isValid($_POST)) {
        R00_Auth::logout(); // a wrapper for Zend_Auth::getInstance()->clearIdentity();

        Zend_Registry::get('Log')->info('User has logged out');

        $this->setRedirect($request); // redirect to the current page
    }

在我的布局中

    echo new R00_Form_Exit();

好的。但它不起作用,我点击表单的提交按钮,页面重新加载但身份仍然存在。

正如我所意识到的,Zend_Form_Element_Hash 每次表单创建时都会生成新的哈希值,并将用户的哈希值与会话的哈希值进行比较——最后生成的哈希值!这很奇怪。例如,即使我尝试在我的应用程序中仅创建一个 R00_Form_Exit,将其存储在注册表中并从中回显,从我的站点“在新选项卡中”打开页面也会导致所有此类受 csrf 保护的表单停止工作。

那么,我该如何保护?

【问题讨论】:

    标签: zend-framework zend-form csrf


    【解决方案1】:

    您应该检查 Zend_Form_Element_Hash 是否能够将散列保存到 Zend_Session_Namespace 中。根据该元素的文档,这是预期的行为:Zend Documentation

    【讨论】:

    • 如何检查?我不知道 :) 顺便说一句,我使用 Zend_Cache 和默认适配器,它使用 Zend_Session,它工作正常。所以我认为没有问题
    • 尝试转储 $_SESSION,它应该包含来自所有 Zend_Session 命名空间的所有值。如果 Hash Form 元素中有某个值,请检查它在两个请求中是否保持相同。
    • 不,不一样 :( ["Zend_Form_Element_Hash_exitsalt_hihacker"] ["hash"] 在一个页面上是“66cb145466361f62c7cf22899cc8807e”,在另一个页面上是“405a7413ff1b58ae966dbfac4a1b9f16”
    • 在ZF_Element_Hash的代码中:$session->setExpirationHops(1)。是的,它每次都会重新生成哈希!呃,要么它不可用,要么我不明白
    【解决方案2】:

    它应该是不同的,每次你调用散列生成器,在你指定的 SESSION 命名空间和位置。这就是为什么您只在构建实际表单时才创建散列。这样做会存储一跳的哈希(意味着页面加载),然后(尤其是)在为用户重新生成表单时忘记它。 这就是 CSRF 的宗旨!通过使过期的表单无效来防止表单劫持。 (基本上)

    如果由于哈希值每次更改而无法“验证”表单,则说明您执行任务的顺序错误,需要重新评估流程。

    【讨论】:

      【解决方案3】:

      在这里我提出我自己的解决方案。但它仍然不是很酷,所以我很高兴听到任何其他更好的解决方案和 cmets。

      我从每个用户的密码哈希和其他参数生成一个哈希。在他更改密码之前,每个用户的哈希值都是不变的:

         public function getSecurityHash() {
                return md5( $this->getIntID() . $this->getLogin() . 'R00SuperSalt' );
         }
      

      并且,形式为:

          $exithash = new Zend_Form_Element_Hidden('exithash');
          $exithash->setValue( R00_Auth::getUser()->getSecurityHash() )
                   ->addValidator('Identical', false, array(
                      R00_Auth::getUser()->getSecurityHash()
                    ))
                   ->setDecorators(array('ViewHelper'))
                   ->setRequired(true);
      

      【讨论】:

      • $exithash->setValue( R00_Auth::getUser()->getSecurityHash() ) ->addValidator('Identical', false, array( R00_Auth::getUser()->getSecurityHash() ) );这不是完全破坏了哈希的效果吗?我的意思是,就像 if("foo" == "foo")
      • 不,它没有 :) 代码在类的 init() 中。在 ->isValid() 中,来自用户的所有输入都填充到表单的值中,因此 ->setValue() 仅影响隐藏元素中的值
      • 这不是 CSRF! CSRF 令牌必须在每个会话的基础上不同(不确定,但甚至可能在每个请求的基础上)。
      猜你喜欢
      • 2018-04-25
      • 2019-04-10
      • 1970-01-01
      • 2018-06-14
      • 2013-01-11
      • 2014-08-31
      • 2014-06-21
      • 1970-01-01
      • 2011-10-19
      相关资源
      最近更新 更多