CSRF Token Not Working with AJAX-Loaded Form

795 views
Skip to first unread message

Jason Williams

unread,
May 9, 2016, 6:42:33 PM5/9/16
to f3-fra...@googlegroups.com
I'm using a CSRF token as part of a form in my app, and I'm having a problem where it always comes back as invalid in the event that form was loaded into the page with AJAX. The relevant parts of my code are below.

Part of the controller function that displays the form:
$session = new \Session();
$f3->SESSION["token"] = $session->csrf();
unset($session);
$f3->pagecontent = "snippet/networkform.php";
$this->generatePage($f3);

Part of the controller function that receives and validates the POST data:
if ($_POST["token"] != $f3->SESSION["token"]) $f3->error(400, "Unexpected data");
$f3->clear("SESSION.token");

Part of the form itself (a view):
<input type="hidden" name="token" value="{{ @SESSION.token }}">

The private function generatePage() from the controller class:
$view = \Template::instance();
if (!$f3->AJAX) echo $view->render("page.php");
else echo $view->render($f3->pagecontent);

What the generatePage function is doing is checking to see how the page was requested. If it's an AJAX request then it renders only the relevant part of the HTML. On the client side, this is inserted into the page with jQuery, leaving all the ancillary parts of the page intact. If it was a synchronous request then the generatePage function renders a complete HTML page including the <head> section, page structure (menu bars, etc) and the content (the form, in this case) itself.

Part of the page.php view:
<include href="{{ @pagecontent }}" />

If I load the form by navigating directly to it's URL with a synchronous request then everything works great. When the form is submitted the CSRF token from the POST data is evaluated against the corresponding session variable, they match, and all is well with the world.

However, if the form content is loaded by AJAX and inserted into an existing page with jQuery, I can see by inspecting the loaded data that a CSRF token is in there, but when the form is submitted it doesn't match the stored session variable. I'm at a loss to figure out why.

To be clear, the form is not (yet) being submitted by AJAX (it will be eventually, but I'm still building). When the user hits submit it's a synchronous POST request to the form handler route, regardless of how the form was loaded.

Any idea what I'm doing wrong?

ved

unread,
May 11, 2016, 11:49:41 AM5/11/16
to Fat-Free Framework
Hi, well, first of all if you're implementing your own csrf token, you can just generate a random string and place it directly into the session using:

$f3->set('SESSION.token', 'sometoken');

You don't need:
$session = new \Session;
That is only needed if you wish to use the cache storage mechanism for sessions so I'm not really sure if initializing that class just to generate a csrf and then unset it is the best choice or if it has any adverse effects on what you're trying to achieve.

Also, this:
$f3->pagecontent = 'something';
While valid, isn't the recommended way of setting framework variables, you should be using:
$f3->set('pagecontent', 'something');
$f3
->get('pagecontent');


Another tip: you can set different routes for ajax or sync requests so you don't need to run that ajax check inside your code, like so:
$f3->route('GET /example [ajax]','Page->getFragment');
$f3
->route('GET /example [sync]','Page->getFull');


As for the actual issue you're having, we'll need some more information.

You say that the AJAX loaded form has the token in there but can you tell us what exactly is happening when you submit that form?
Is the token you saw on inspect being submitted correctly? Is it the token on the session that changes?

You can use the network inspector to determine what exactly is being posted to the server. (or alternatively just use a var_dump or something in code)

ikkez

unread,
May 11, 2016, 6:30:07 PM5/11/16
to Fat-Free Framework
I think, when the AJAX request also creates the session handler, the internal csrf token might have been updated, so it does not match to the value in the form anymore.
One solution would be to not create the session in the ajax request if possible, or transfer the new token via this ajax request as well and update the value in the hidden form.
This is nearly the same mechanic as if you open the same page with a captcha form in two browser tabs, and then go back to the first, enter the captcha and submit... will not work, because the captcha reference in the session was already updated while opening the 2nd tab, so the first became invalid.

Jason Williams

unread,
May 15, 2016, 9:04:24 PM5/15/16
to Fat-Free Framework
Thanks both for the responses. The network inspector was key, and the error was in my javascript. I was accidentally requesting content twice but only using it when it was first returned, the result being that the CSRF token was reset before it could be used.

I have another question. Why is
$f3->pagecontent = 'something';
not the recommended way of setting framework variables?

That syntax feels cleaner to me, so I'm just looking to understand why $f3->set() is preferred.

bcosca

unread,
May 16, 2016, 3:37:35 AM5/16/16
to Fat-Free Framework
$f3->pagecontent='something';

will internally call the following in sequence:

1. $f3->__set('pagecontent','something');
2. $f3->set('pagecontent','something');

As you see, there's an intermediate magic call to save to save the same value.

ved

unread,
May 16, 2016, 10:56:51 AM5/16/16
to Fat-Free Framework
Hey, I personally didn't know about this.

I was just assuming that by setting properties as the OP mentioned it would just end up "polluting" the $f3 object with lots of properties that could maybe eventually clash with some already defined property.

In this case I assume there's no real issue setting fw variables like that.
Reply all
Reply to author
Forward
0 new messages