Thursday, March 26, 2009

A More Secure Way to Transfer Session State Between CakePHP Applications

Thursday, March 26, 2009

In a previous article entitled "Sharing Session State Across CakePHP Applications", I wrote about the way to transfer session IDs between CakePHP applications. Thanks to the CakePHP development team, CakePHP already has secure session handling; however, some might even think that it is not secure to append a session ID to links. I hear say some search engines indexes URLs with session IDs. Isn't it horrible? One obvious solution would be not to assign them to any of the links on pages.

Let's say we have two sites, siteA.com and siteB.com. We need to maintain a user's session state (authenticated with the Auth Component) when the user jumps from siteA.com to siteB.com by clicking some link.

First, we need to do some settings:

  • Set 'Security.level' to 'low' on siteB.com.
  • Set the session handling method ('Session.save' in app/config/core.php) to 'database'.
  • Use the same Security.salt (/app/config/core.php) for each application.

We are going to use the same session database table for both sites.

Use the CakePHP console to create your session database table:

$ cake schema run create Sessions

Yeah, it's always fun to run the cake console, but you can also use the SQL file found in app/config/sql/sessions.sql.

Create a SiteTransfer Model for each application (on siteA.com and siteB.com):

class SiteTransfer extends AppModel {
    var $name = 'SiteTransfer';
}

Basically the table structure looks something like this:

create table site_transfers (
 id varchar(36) not null,
 sess_id varchar(26) not null,
 primary key (id)
);

In our Users Controller on siteA.com:

class UsersController extends AppController {

  var $name = 'Users';
  
  function index() {}

  function redirectem() {
    $this->autoRender = false;
    App::import('Core', 'String');
    $data['SiteTransfer']['sess_id'] = $this->Session->id();
    $this->SiteTransfer->id = String::uuid();
    if($this->SiteTransfer->save($data)) {
      $this->redirect(
        'http://siteB.com/users/catchem?uuid='.$this->SiteTransfer->id
      );
    }
  }

} 

We have a link saying ‘Go to siteB.com’in the index.ctp view:

echo $html->link('Go to siteB.com', array(
  'action' => 'redirectem'
));

Let's see what's going on here…
When the user on the Index page on siteA.com clicks the link, we redirect the user to the redirectem() action (this action does not need any view). In the 'redirectem' method, we get the current session ID and save it into our site_transfers table with a UUID (i.e. String::uuid). Then we do a redirect to /users/catchem on siteB.com.

Alright, let's build a Users Controller for siteB.com:

class UsersController extends AppController {

  var $name = 'Users';
  var $components = array('Session');

  function beforeFilter() {
    if(!empty($this->params['url']['uuid'])) {
      $uuid = $this->params['url']['uuid'];
      $data = $this->SiteTransfer->findById($uuid);
      $this->Session->id($data['SiteTransfer']['sess_id']);
      $this->SiteTransfer->del($uuid);
    }
  }
  
  function catchem() {
    $this->redirect(array('action' => 'index'));
  }

  function index() {
    pr($this->Session->read('Auth')); 
    exit; 
  }

} 

What happens when the user gets to siteB.com?.
We search our database table (site_transfers) for the UUID token, and then instantiate the session with the session ID from the database. Finally, for security purpose, we need to delete the UUID and session ID from our site_transfers table.

All done! So now the user is logged into both siteA.com and siteB.com.

2 comments:

  1. thanks for adding my blog. now i know who to contact if i ever need help programming something.

    -Jaime

    ReplyDelete
  2. Thank you for sharing this. I've been trying to find a way to set up a single login for multiple apps. This is exactly what I needed to do it.

    ReplyDelete

Please feel free to post your comment about this article.

 
JamNite ◄Design by Pocket, BlogBulk Blogger Templates