We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

CSRF protection not working

Um using version 1.2.3 and 1.2.4 . I'm making a simply invitation form using CSRF just like the tutorial https://github.com/phalcon/docs/blob/master/en/reference/security.rst#cross-site-request-forgery-csrf-protection

But for some reason the validation $this->security->checkToken() is always returning false.

I saw this post with a similar case but no reply https://forum.phalcon.io/discussion/310/csrf-protection

Anyone having this issue?

Thanks



98.9k

This happens when the token is regenerated before it's checked, you must generate the token in the form: https://github.com/phalcon/vokuro/blob/master/app/views/session/signup.volt#L53



16.4k
edited Mar '14

Im already generating the token in the form , and it still not working

<!-- login beta -->
<div id="beta_login">
    <?php echo $this->tag->form('') ?>
        <div>
                <?php echo $this->tag->textField(["name", "size" => "30", 'class' => 'tfield', 'placeholder' => 'Dirección de correo electrónico...']); ?>

                <input type="hidden" name="<?php echo $this->security->getTokenKey() ?>" value="<?php echo $this->security->getToken() ?>"/>
            <input type="submit" class="btn g_blue" value="Enviar" />
        </div>
    </form>
</div>
<!-- END login beta -->


16.4k

Any update?



98.9k

Could you try using a static name for the input:

<input type="hidden" name="csrf" value="<?php echo $this->security->getToken() ?>"/>


16.4k
edited Mar '14

Nop not working

If i do this on the index.phtml on the main view

<!-- login beta -->
<div id="beta_login">
    <h2 class="innerS">¡Regístrate para la versión beta!</h2>
    <?php //echo $this->tag->form('') ?>
    <?php echo $this->tag->form("invites/create") ?>
        <div>
                <?php echo $this->tag->textField(["name", "size" => "30", 'class' => 'tfield', 'placeholder' => 'Dirección de correo electrónico...']); ?>
                 <input type="hidden" name="<?php echo $this->security->getTokenKey() ?>" value="<?php echo $this->security->getToken() ?>"/>

                <input type="submit" class="btn g_blue" value="Enviar" />
        </div>
    </form>
</div>
<!-- END login beta -->

It doesnt work but if I use the token on another controller/view that is not the index, it work perfectly :S



16.4k

Ok so , this is too weird,

If i have the form running with token at index.phtml on the IndexController , tokens wont work on any place of the system. But if I dont use the token on that main function, it will work everyplace else , any one else having this issue?



16.4k

Ok found a solution, change the index in the route to another function and everything will work currently, please try it yourself @Phalcon and let me know if you can reproduce the error



558

Hello there, I've had the same problem. I'd really like to have a sign up and/or login form on my main site. I've tried but always get a csrf validation error. Is there a possibility to make csrf work on the index page?



16.4k

hey @Nate, I dont use token on index function if you change the index to lets say home function it will work



558

Hey @kaioken, That's what I figured. It just would be nice to have an official answer on that topic and my curiosity would love to know why it's behaving that way :p



8.1k
edited Mar '14

CSRF is working. For example. Token generated in form with:


  protected function setFields() {
        $attributes = $this->modelsMetadata->getAttributes($this->entity);
        $metadata   = $this->annotations->get($this->entity);
        foreach ($attributes as $attribute) {
            $this->fields[$attribute] = $metadata
                    ->getPropertiesAnnotations()[$attribute]
                    ->get('FormOptions')
                    ->getArguments();
        }
        foreach ($this->fields as $field => $type) {
            $fieldtype  = array_shift($type);
            $fieldclass = $this->classprefix . $fieldtype;
            $this->add(new $fieldclass($field, $type));
            if ($fieldtype !== 'hidden') {
                $this->get($field)->setLabel($this->get($field)->getName());
            }
        }
        $this->add(new \Phalcon\Forms\Element\Hidden($this->security->getTokenKey(), [
            'value' => $this->security->getToken(),
        ]));
        $this->add(new Submit('submit', [
            'value' => 'Send',
            'class' => 'submit-btn',
        ]));
    }

    public function renderform() {
        $this->html .= $this->tag->form([$this->action,
            'id' => $this->prefix . 'form',
        ]);
        //fill form tags
        foreach ($this as $element) {
            $this->html .= '<div class="row-form">';
            $this->html .= '<label for="';
            $this->html .= $element->getName() . '">';
            $this->html .= $element->getLabel() . '</label>';
            $this->html .= $element;
            $this->html .= $this->messageCollect($element);
            $this->html .= '</div>';
        }
        $this->html .= $this->tag->endForm();
        return $this->html;
    }

And we check them in controller :

 /**
     * @Route("/new", methods={"GET", "POST"})
     */
    public function newAction() {
        if ($this->request->isPost() && $this->security->checkToken()) {
            $discussform            = new DiscussForm();
            if ($discussform->isValid($this->request->getPost())) {
                $this->view->disable();
                $discussion = new Discussions();
                $discussform->bind($this->request->getPost(), $discussion, [
                    'users_id',
                    'categories_id',
                    'title',
                    'topic',
                ]);
                $discussion->save();
                $this->response->redirect();
            }
        }
        $this->assets->get('styles')->addCss('assets/css/form.css');
        $discussform             = new DiscussForm();
        $this->view->discussform = $discussform;
    }

You must check token in your controller (action) before any operation, wich may generate new token.



558

Thanks @oleg578. Maybe I need to do it this way to make it work. I was using the technique used in Vökuró. It works great except on the index controller. But I'll implement it like you suggested and make it work :D



8.1k

I do not advise Vokuro regarded as a tutorial. It's my opninion. It was created when the Phalcon was 0.9. Just need to detail and slowly read the documentation. And several times, preferably :) In my experience - helps :)



558

Hehe. Will do. And build my stuff from scratch according to the docu :D Thanks!

@oleg578 Do you have idea what php.ini setting related to when my csrf is not working?

I've tried many ways (including yours) but still no luck.

Thanks.



8.1k

At start check your server and CGI for work with session without Phalcon. After play with session via Phalcon documentation.



785
edited Mar '14

thanks, @Max Castro changing "/" route to something different than index/index worked perfectly

however i found an interesting behavior of beforeExecuteRoute($dispatcher), flashing error once will displayed twice in the page i believe it has something to do with csrf also when in is not working, because is trigerred twice

does somebody has an explication why a single flash in before route is displayed twice?



16.1k

Wow... this is a super bizarre issue. How many of you with this issue are using Chrome?

Here's what I've discovered. In Safari and Firefox if I clear my cookies/browsing history everything works as expected. Thats reasonable. But heres the weird part:

Im on page /author/dashboard and have a link in which I use getToken to generate a $_GET variable to /session/logout?tok=xxxx... My default controller is "SiteController" and default action is "main." If I even HAVE an indexAction my csrf links are wrong... I'm not on the default route, nor do I have the default route set to /site/index... Merely having indexAction in SiteController messes up my csrf links in Chrome -- even though its a complete separate controller than what I'm on!

@phalcon can you speculate on this?



278
edited Mar '14

FINALLY after a long search I found it via the Zend CSRF bug in Chrome. What Chrome does is request the favicon.ico in the background. If it does not find it, it requests the page again and you get a new csrf token. The fix is adding the favicon.ico

ALSO: turn off the Web Inspector Tool. The CSRF check does not work with the toolbar open.

This bug I do not have in Firefox.

edited Mar '14

Hi,
It is true :) not only favicon.ico, robots.txt, and any other.
At http server you can eliminate this bug too.
Below for nginx

server {
    #listen, servername, root etc

    location = /robots.txt { return 204; access_log off; log_not_found off; }
    location = /favicon.ico { return 204; access_log off; log_not_found off; }
    location ~ /\. { access_log off; log_not_found off; allow all; }
    location ~ ~$ { access_log off; log_not_found off; deny all; }

    # other locations, for php, error_page etc
}


785
edited Mar '14

@warmwaterkruik if you close the firefox browser and open again it will reproduce as in chrome (but only once, next times will work). Thanks for discovering that!

R



98.9k

Thank you, this MUST be added to the docs :)

Some times ago I had problem with flashSession the same reason. I though that flashSession didn't work but it was flushed by favicon. Some hours lost :(

A bit of a necro post, but I thought I'd add my Apache config to mirror the nginx config posted above:

RedirectMatch 204 /robots.txt
RedirectMatch 204 /favicon.ico


8.1k

This also happens in Firefox with Firebug enabled and browser cache disabled.

The page is requested twice, with the 2nd page request being done after all other requests.

This must mean then that the result of the first request is shown in the browser (with the 2nd one f*ing up the token in the session of course.)

Thank you, only add favicon and all nice .

FINALLY after a long search I found it via the Zend CSRF bug in Chrome. What Chrome does is request the favicon.ico in the background. If it does not find it, it requests the page again and you get a new csrf token. The fix is adding the favicon.ico

ALSO: turn off the Web Inspector Tool. The CSRF check does not work with the toolbar open.

This bug I do not have in Firefox.

edited Nov '15

I have been experiencing this problem. What we are all trying to do here is make sure that a second request isn't made to the same controller and action as is sent when the page loads. What is causing this problem is requests being made for files that are not there. What I found the solution was is logging the controller name and action name everytime a request is made to find out which files are being requested and not found..

For many, this will be the favicon.ico or the robots.txt file.

For me the problem was a little different:

I was hosting a version of jquery on my server at js/jquery.min.js. What I found is that everytime I loaded a page that used this jquery file a request was made looking for js/jquery.min.map and since I didn't have that file in the js directory it went looking for it inside phalcon by looking for the js controller with the jquery.min.map action. When it couldn't find that another request was sent to the same controller and action as before. In my case this could have been index/index or login/index, it could have been any combination. The solution was for me to put in the jquery.min.map file from code.jquery.com/jquery.min.map. Then the same thing happened again where requests were made for jquery.js ( This is the web inspectors looking for these ). So I put in the jquery.js from code.jquery.com/jquery.js. After doing that there were never any unwanted requests made again.

The point here is that you need to make sure that all files that are being requested are found, or you need to change you code so that those files are not requested at all ( you could have just loaded jquery from the jquery domain instead of your own web server ). In order to check out which files are being requested and not found, make sure to log dispatcher information like so:

    $controller = $dispatcher->getControllerName ();
    $action = $dispatcher->getActionName ();
    Phalcon\DI::getDefault()->get('logger')->debug( $controller .' '. $action );

What my log showed when I was experiencing the issue:

    [Tue, 24 Nov 15 19:32:00 +0000][DEBUG][][] index index
    [Tue, 24 Nov 15 19:32:00 +0000][DEBUG][][] js jquery.min.map
    [Tue, 24 Nov 15 19:32:00 +0000][DEBUG][][] index index