Ben Murden

Tag: PHP

CakePHP, Rolling Session IDs, and Concurrent Ajax Calls

by on May.04, 2010, under Development

Picture a set of buttons in a list that allow users to quickly cancel listed bookings. These buttons make an Ajax call to a controller action with the relevant ID passed as a parameter. I already have a jQuery method turning these buttons – generated with Cake’s HtmlHelper->Link() – into Ajax calls using the anchor’s href as the URL.

By itself, each button works perfectly. The problem shows itself when a user tries to cancel multiple bookings concurrently. The controller action will process the first request, but choke on all the rest and return a blank response.

This is a very specific problem I had encountered with CakePHP, but I think with Ajax becoming the norm for web application interaction, it’s worth mentioning this fix. The problem lies in the way session IDs are regenerated between requests in Cake’s highest security setting. An easy fix would be to set Cake’s core security level to medium, but we care too much about security, right? So we’ll make a reusable fix that allows us to overcome this issue without affecting security across the board.

In the controller:

function beforeFilter() {
    // Fix for concurrent Ajax requests
    // If it's an Ajax request and the session id has been passed, retain it
    if ($this->RequestHandler->isAjax() && !empty($this->params['url']['s'])) {
      $this->Session->id($this->params['url']['s']);
      $this->Session->start();
    }
    parent::beforeFilter();
  }

This is a modification of a solution given by Adam Royle, changing the process to be more reusable with an optional querystring variable that indicates we want to keep the session ID during an Ajax call. This means if you don’t expect concurrent requests, you can forego the querystring and your action will perform as normal without it, regenerated session IDs included.

The link can then be generated like this:

echo $html->link(__('Cancel', true), array('controller' => 'bookings', 'action' => 'cancelBooking', '?' => array('s' => $session->id()), $booking['id']), array('id' => 'cancelLink'.$booking['id']));

Thanks be to Adam Royle for discovering the root of the issue.

Improvements

This is still not a perfect solution.

  • It effectively exposes the temporary session ID to the user.
  • It will also still fall over if the session ID is regenerated by another Ajax call on the same page, meaning all Ajax buttons in a single view must have this logic.
  • If the user navigates away and performs other actions, then returns to this page using the browser’s back button – and therefore, the local cache – the session ID will be wrong again and cause a logout.

Other possible solutions:

  • Queueing Ajax requests in jQuery so the session ID can regenerate as needed.
  • Session handler in Cake is modified to not regenerate session IDs when request is from an XML/HTTP Request Object.

If there are any other ways of preventing newly generated session IDs from messing with concurrent Ajax requests, I’d love to hear them!

5 Comments :, , , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!