ZF-10714: Form validation (Zend_Form_Element_Hash) in Google Chrome

Description

Hello, i have a problem with element Zend_Form_Element_Hash. In Google Chrome (7) form validation report me a error : "No token was provided to match against", but in FF or IE it works fine.

Comments

Could you provide some more information about how to reproduce this error?

I use chrome daily for my development work, and have never had this issue.

If this is a support question, could you direct it to the fw-general mailing list, or to #zftalk on freenode IRC

I don't think, this is an issue of ZF! I've had this and other problems in the past with Google Chrome. This browser caches so freakin' hard, that it comes to such errors in development! Please delete all your internet data in chrome and restart at least your browser (if not the whole OS). That should normally do the job!

Added issues I think are related to this issue.

The same issue haunted me in the beginning of the week. However, in my case I had two forms in the site: Feedback and Order. Both of these forms had hash element. In the latest version of Firefox, Feedback form worked but Order form didn't. In Chrome and IE both forms worked. I was unable to track the problem and it seems to be very hard to isolate. The only solution atm. was to remove the hash element from the Order form.

I have had similar problems in ZF-5182, but at this time neither of these forms were placed at the landing page (the first page of the site).

I've had the same problem with the Chrome browser. The issue is not really a bug in ZF but in redirects, in my case the Zend_Auth/Acl code. When Chrome looks for pages to cache and is redirected to the current page the hash fails to validate.

In my code I've found that when I have a redirect to a form, usually the case with login forms I get the token error, I've now changed the redirect to an login error in the ErrorController and that seems to work better.

Below is some scaled down code that I've used when debugging this issue if you want to try it out yourselves:


class IndexController extends Zend_Controller_Action {
    public function indexAction() {
        $form = new Form_Index_Test();
        if ($this->getRequest()->isPost()
                && $form->isValid($this->getRequest()->getPost())) {
            die("Yes!");
        }

        $this->view->form = $form;
    }
}


class LoginController extends Zend_Controller_Action {
    public function userAction() {
        $form = new Form_Login;

        if ($this->_request->isPost()) {
            if ($form->isValid($this->_request->getPost())) {
                die("Logged in!");
            }
        }

        $this->view->form = $form;
    }

}


class ErrorController extends Zend_Controller_Action
{

    public function errorAction()
    {
    }


    public function privilegesAction()
    {
        die("You lack the privileges!");
    }

    public function loginAction()
    {
        die("Login please!");
    }

}


class Form_Index_Test extends Zend_Form {

    public function __construct($options = null) {
        parent::__construct($options);
        $this->setName('form_show_dates');

        $this->addElement('Text', 'test', array('label' => 'Text'));

        $this->addElement('hash', 'csrf');

        $this->addElement('Submit', 'submit', array('label' => 'Save'));
    }

}


class Form_Login extends Form_Base {

    public function init() {

        $this->setName('form_login');

        $length_message = 100;
        $el_name = $this->createElement('ValidationTextBox', 'username')
                        ->setRequired(true)
                        ->setRegexp('[\w\d\s\@]+')
                        ->addValidator('stringLength', true, array('min' => 2, 'max' => $length_message))
                        ->addFilter("stringTrim")
                        ->setLabel('Uname');

        $length_message = 255;
        $el_password = $this->createElement('PasswordTextBox', 'password')
                        ->setRequired(true)
                        ->setRegexp('[\w\d\s\@]+')
                        ->addValidator('stringLength', true, array('min' => 2, 'max' => $length_message))
                        ->addFilter("stringTrim")
                        ->setLabel('Pwd');

        $el_submit = $this->createElement('Submit', 'submit')
                        ->setLabel(__('Save'))
                        ->setOrder(998);

        $this->addElements(array($el_name, $el_password, $el_submit));
    }
}

abstract class Form_Base extends Zend_Dojo_Form {

    public function __construct($options = null) {
        parent::__construct($options);

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


class Plugin_Controller_Auth extends Zend_Controller_Plugin_Abstract {

    /**
     * @var Zend_Auth
     */
    private $_auth;
    /**
     * @var Zend_Acl
     */
    private $_acl;
    private $_noauth = array('module' => 'default',
        'controller' => 'error',
        'action' => 'login');
    private $_noacl = array('module' => 'default',
        'controller' => 'error',
        'action' => 'privileges');

    public function __construct($auth, $acl) {
        $this->_auth = $auth;
        $this->_acl = $acl;
    }

    /**
     * Performs the login etc...
     *
     * @param Zend_Controller_Request_Abstract $request
     * @return void
     */
    public function preDispatch($request) {
        if ($this->_auth->hasIdentity()) {
            $role = $this->_auth->getIdentity()->getRole();
        } else {
            $role = 'guest';
        }

        $resource = $controller = $request->controller;
        $action = $request->action;
        $module = $request->module;

        $options = $request->getParams();


        if (!$this->_acl->has($resource)) {
            $resource = null;
        }

        if (!$this->_acl->isAllowed($role, $resource, $action)) {
            if (!$this->_auth->hasIdentity()) {
                $module = $this->_noauth['module'];
                $controller = $this->_noauth['controller'];
                $action = $this->_noauth['action'];
            } else {
                $module = $this->_noacl['module'];
                $controller = $this->_noacl['controller'];
                $action = $this->_noacl['action'];
            }

            $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
            $redirector->gotoSimpleAndExit($action, $controller, $module);
        }
    }

}

class Security_Acl extends Zend_Acl {
    public function __construct() {
        $this->add(new Zend_Acl_Resource('index'));
        $this->add(new Zend_Acl_Resource('login'));
        $this->addRole(new Zend_Acl_Role('guest'));
        $this->allow('guest', 'index');
        $this->allow('guest', 'login', 'user');
    }
}

If you want to see the error when logging in use:


    private $_noauth = array('module' => 'default',
        'controller' => 'login',
        'action' => 'user');

Does this problem still persist? If so, is it with the framework or the browser? A lot of these wishy-washy Zend_Session-related bugs appear to be falling into the "concurrent requests returning 404 errors cause competing sessions to be initiated" issue (primarily with Google Chrome)

I've having the same issue. In chrome, if I wait over 5 mins. i get "No Token was provided to match against" I can submit again and it works. If i submit the form straight after loading the page it works fine.

with IE. no need to wait, just get this error: "The two given tokens do not match" after submitting twice it works.

I set the TTL. via timeout. I did this and it worked for chrome and IE:

$this->addElement('hash', 'csrf', array( 'ignore' => true, 'timeout' => 60000, ));