开发者

Zend CSRF protection token doesn't work in Chrome

开发者 https://www.devze.com 2023-01-23 11:19 出处:网络
So here is a method I\'m using to create form objects: protected function _getForm($form, $action = null)

So here is a method I'm using to create form objects:

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;
}

As you can see, I am creating a random string token and adding a My_Validator_CSRF() validator to the form. Here is the validator:

<?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 val开发者_JAVA百科id 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;
     }
}

This works great in Firefox and IE but in Chrome I keep getting the error message:

"b6be61a6aece979d15eb1f605e109f32" is not valid token

And the token changes after each page refresh. Which it does not in Firefox and IE. Am I doing something wrong? Here is how I start the session:

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();


I'm wondering why you don't want to use ZendFramework's Hash element, hm?


This issue appears also for Zend_Form_Element_Hash - ZF-10714 seems to be the same issue - everything works with Firefox and IE but not in Chrome where I keep getting the "The two given tokens do not match" validation failure.

Background - debugging

When stripping down my application for debugging I managed to locate the error to redirects. When Chrome tries behind the scenes cache the next page it triggers a new CSRF in the same session overwriting the old CSRF token. The major culprit was in my case my controller pluging dealing with the Zend_Auth/Acl part where I redirected to the login page if the browser session (the user) wasn't logged in and got Chrome to trigger a new CSRF for the login - making it impossible to login on Chrome.

My solutions today are:

All Auth/Acl forwarding is to the ErrorController

If a user lacks permissions for a certain action he/she is asked to go to the login page through a separate link in the ErrorController loginAction() or permissionAction()

The CSRF token id is unique for each form

I extend the forms with a custome Zend_Dojo_Form class:

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);
    }
}

I hope this helps, this problem annoyed at least me immensely and I'm glad that I finally seem to have the problem under control. You can also check out the ZF bug where I've posted most parts of my debug controller.


The problem is because chrome caches all files and maybe some of them are not present.

For example if your html is something like this:

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

And the favicon.ico is not present then when google chrome attempts to cache the favicon.ico will call the http://your.domain/favicon.ico. This will generate a page that includes your form because the request is passed to your bootstrap and will get an error "favicon.ico controller not found".

In order to see this problem you can access http://your.domain/favicon.ico and see the result for yourself.

You can fix this problem by adding the favicon.ico to your web directory or by stopping the Login form to be created when the error controller is invoked.


I had a similar problem, but unfortunately none of the answers above helped me.

In my case i had to additionally change the htaccess to this:

RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php

as Alfredo Granda mentioned above, there is a second request for the favicon.ico. Unfortunately the rewrite rule that's recommended by the zend framework forwarded the favicon request to the index, even if the favicon existed.

Hope that this helps! (it helped me, fortunately)


I had the same issue and could fix it by simply creating and adding a favicon to the /public-directory.

I think the problem was that Chrome tries to load the favicon automaticly from /public and if it's not available it causes a call to http://thedomainyouhavechosen.foo/favicon.ico (as Alfredo Granda already said) and triggers a new CRSF which overwrites the old one.

Hope this could help.


If you're using the Firebug plugin on Chrome (or similar plugin), it makes two requests for the page, so the hash fails. Deactivate the plugin and try again.

0

精彩评论

暂无评论...
验证码 换一张
取 消