Zend CSRF保护令牌在Chrome中不起作用

时间:2010-11-05 14:14:55

标签: php zend-framework

所以这是我用来创建表单对象的方法:

protected function _getForm($form, $action = null)
{
    require_once(APPLICATION_PATH.'/modules/'.$this->_request->getModuleName().'/forms/'.$form.'.php');
    $form = new $form();
    if (null !== $action) {
        $form->setAction($action);
    }

    $csrfNamespace = new Zend_Session_Namespace('Tokens');
    if (false === isset($csrfNamespace->csrfToken)) {
        $csrfNamespace->csrfToken = $this->_helper->randomString();
    }
    $csrfToken = new Zend_Form_Element_Hidden('csrf_token');
    $csrfToken->setValue($csrfNamespace->csrfToken)
              ->addValidator(new My_Validator_CSRF())
              ->removeDecorator('HtmlTag')
              ->removeDecorator('Label'); 
    $form->addElement($csrfToken);

    return $form;
}

如您所见,我正在创建一个随机字符串标记并向表单添加My_Validator_CSRF()验证器。这是验证器:

<?php
class My_Validator_CSRF extends Zend_Validate_Abstract
{
    const TOKEN_NOT_SET = 'notSet';
    const TOKEN_INVALID = 'invalid';

    protected $_messageTemplates = array(
        self::TOKEN_NOT_SET => "'%value%' cannot be compared to anything, token has not been generated",
        self::TOKEN_INVALID => "'%value%' is not valid token"
    );

    public function isValid($value)
    {
        $this->_setValue($value);

        $isValid = true;

        $csrfNamespace = new Zend_Session_Namespace('Tokens');

        if (false === isset($csrfNamespace->csrfToken)) {
            $this->_error(self::TOKEN_NOT_SET);
            $isValid = false;
        }

        if ($csrfNamespace->csrfToken !== $value) {
            $this->_error(self::TOKEN_INVALID);
            $isValid = false;
        }

        return $isValid;
     }
}

这在Firefox和IE中效果很好但在Chrome中我一直收到错误消息:

"b6be61a6aece979d15eb1f605e109f32" is not valid token

每次刷新页面后令牌都会更改。它在Firefox和IE中没有。难道我做错了什么?以下是我开始会议的方式:

ini_set('session.cookie_secure', 1);
ini_set('session.cookie_httponly', 1);
ini_set('session.use_only_cookies', 1);
// start the session
Zend_Session::start();

6 个答案:

答案 0 :(得分:6)

我想知道为什么你不想使用ZendFramework's Hash element,hm?

答案 1 :(得分:2)

此问题也出现在Zend_Form_Element_Hash中 - ZF-10714似乎也是同一个问题 - 一切都适用于Firefox和IE但不适用于Chrome,我不断得到“两个给定的令牌不匹配”验证失败。

后台 - 调试

当剥离我的应用程序进行调试时,我设法找到重定向的错误。当Chrome尝试在后台缓存下一页时,它会在同一会话中触发新的CSRF,从而覆盖旧的CSRF令牌。主要的罪魁祸首就是在我的情况下我的控制器插件处理Zend_Auth / Acl部分,如果浏览器会话(用户)没有登录,我将重定向到登录页面并让Chrome触发新的CSRF进行登录无法登录Chrome。

我今天的解决方案是:

所有Auth / Acl转发到ErrorController

如果用户缺少某项操作的权限,则会要求他/她通过ErrorController loginAction()或permissionAction()中的单独链接转到登录页面

每个表单的CSRF令牌ID都是唯一的

我使用客户Zend_Dojo_Form类扩展表单:

abstract class Form_Base extends Zend_Dojo_Form {
    public function __construct($options = null) {
        parent::__construct($options);

        $token_id = "csrf_token_id_" . strtolower(get_called_class());
        $this->addElement('hash', $token_id);
    }
}

我希望这会有所帮助,这个问题至少让我非常恼火,我很高兴我终于可以控制住这个问题了。您还可以查看我发布调试控制器大多数部分的ZF bug

答案 2 :(得分:2)

问题是因为chrome缓存了所有文件,可能还有一些文件不存在。

例如,如果你的html是这样的:

<html>
    <head>
        <link href="/favicon.ico" rel="shortcut icon" />
    </head>   
    <body>
        <form id="login">
            // form elements here
        </form>
    </body>
</html>

并且当Google Chrome尝试缓存favicon.ico时,favicon.ico不会出现,而是会调用http://your.domain/favicon.ico。这将生成一个包含您的表单的页面,因为请求被传递给您的引导程序并将收到错误“找不到favicon.ico控制器”。

要查看此问题,您可以访问http://your.domain/favicon.ico并亲自查看结果。

您可以通过将favicon.ico添加到您的网络目录或通过在调用错误控制器时停止创建登录表单来解决此问题。

答案 3 :(得分:1)

我遇到了类似的问题,但不幸的是,上述答案都没有对我有所帮助。

在我的情况下,我必须另外将htaccess更改为:

RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
正如Alfredo Granda上面提到的那样,还有对favicon.ico的第二个请求。不幸的是,即使存在favicon,zend框架推荐的重写规则也会将favicon请求转发给索引。

希望这有帮助! (幸运的是,它帮助了我)

答案 4 :(得分:1)

我遇到了同样的问题,只需在/ public-directory中创建并添加一个favicon即可解决问题。

我认为问题在于Chrome会尝试从/ public自动加载favicon,如果它不可用,则会调用http://thedomainyouhavechosen.foo/favicon.ico(正如Alfredo Granda已经说过的那样)并触发新的CRSF覆盖旧的之一。

希望这可以提供帮助。

答案 5 :(得分:0)

如果您在Chrome(或类似的插件)上使用Firebug插件,则会对该页面发出两个请求,因此哈希失败。取消激活插件,然后重试。