Fork me on GitHub

CakePHP Rainbow Table Protection Behaviour

CakePHP Rainbow Table Protection Behaviour

So after looking over some security techniques and discovering the quite interestingly named Rainbow table password cracking mechanism I decided to look into a way around this table password cracker. The default way that CakePHP hashes its passwords into the database is via hashing from a single salt string in core.php. This is a lot better than no salt string, however if someone was keen/bored/lame enough to get to work a rainbow table based on your single salt string then all the passwords/hashed-data stored in your tables would become readable.

**Please note use this post as a guide below. For updated code please see my github repository page as this contains updated code & guides**

GitHub: http://github.com/voidet/grey_tables

A way around this is to add a unique salt string per record, along with the CakePHP’s salt string from core.php. This was a rainbow table would have to be constructed based on a per record basis, and not just on a table. This would mean that the lamer would have to wait a few lifetimes to crack all the passwords in your database table, or wait a long time to crack a single user’s. So my initial behaviour is as follows, and it does require some work arounds:

<?php

class GreyTablesBehavior extends ModelBehavior {

function setup(&amp;$model, $settings = array()) {
$default = array('field' => 'salt');

if (!isset($this->settings[$model->name])) {
$this->settings[$model->name] = $default;
}

$this->settings[$model->name] = array_merge($this->settings[$model->name], ife(is_array($settings), $settings, array()));
}

function beforeFind(&amp;$model, $queryData) {
if(!empty($queryData['conditions'][$model->name.'.password']) &amp;&amp; !empty($queryData['conditions'][$model->name.'.username'])) {
$user_id = $this->findSaltedUser($model, $queryData['conditions']);
if (!empty($user_id)) {
unset($queryData['conditions']);
$queryData['conditions'] = $user_id;
}
}
return $queryData;
}

function beforeSave(&amp;$model) {
if (empty($this->id) &amp;&amp; !empty($model->data[$model->name])) {
$data = &amp;$model->data[$model->name];
$data['password'] = $this->generateSaltedPassword($data['password'], $data[$this->settings[$model->name]['field']]);
}
return parent::beforeSave(&amp;$model);
}

function generateSaltedPassword($password = '', $saltString) {
if (!empty($password)) {
return Security::hash($password.$saltString, null, false);
}
}

function findSaltedUser(&amp;$model, $fields = array()) {
if (!empty($fields)) {
$user_id = $model->query('SELECT `'.$model->name.'`.`id` as 'id' FROM '.$model->table.' as '.$model->name.' WHERE `'.$model->name.'`.`username` = ''.$fields[$model->name.'.username'].'' AND `'.$model->name.'`.`password` = SHA1(CONCAT(''.$fields[$model->name.'.password'].'', `'.$this->settings[$model->name]['field'].'`)) LIMIT 1');
if (!empty($user_id)) {
$fields[$model->name.'.id'] = $user_id[0][$model->name]['id'];
unset($fields[$model->name.'.password'], $fields[$model->name.'.username']);
}
}
return $fields;
}

function hashPasswords(&amp;$data, $alias) {
if (isset($data[$alias]['password'])) {
$model->data = $data;
$model->data[$alias][$this->settings[$alias]['field']] = Security::hash(String::uuid(), null, true);
$model->data[$alias]['password'] = Security::hash($data[$alias]['password'], null, true);
return $model->data;
}
return $data;
}

}

?>

Now in your model you would do something like:

<?php

class Member extends AppModel {

var $actsAs = array('GreyTables');

function hashPasswords($data) {
return $this->Behaviors->GreyTables->hashPasswords($data, $this->alias);
}

}

?>

It’s a simple work around to get the behaviour to use the hashPasswords method automatically called. I am yet to do any extensive benchmarks on this code, and will have to include more settings in regards to how the salting is handled, and also the default username/password/email fields to be used to check the Auth. That will come soon. There is one flaw however, in that the conditions are constructed in two parts. The first part looks for the resalted username/password combo, then the second part uses the id found from that query and follows up with any other conditions to be used for the query. For example:

First query:

SELECT `Member`.`id` as 'id' FROM members as Member WHERE `Member`.`username` = 'testuser' AND `Member`.`password` = SHA1(CONCAT('1e71a44447c4e3ea05e8f81f031702f00d19c48e', `salt`)) LIMIT 1

Second query:

SELECT `Member`.`id`, `Member`.`username`, `Member`.`email`, `Member`.`password`, `Member`.`salt`, `Member`.`active`, `Member`.`created`, `Member`.`modified` FROM `members` AS `Member` WHERE `Member`.`active` = 1 AND `Member`.`id` = 52 LIMIT 1

I will be working to make the second query work as a read via the Id, as apposed to extra conditions.
More to come!

Posted by voidet

Categorised under CakePHP
Bookmark the permalink or leave a trackback.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

or
Links:nike air max pas chernike air max pas chernike tn pas cherray ban pas chernike air max pas chernike tn pas cherray ban pas cherray ban pas cherray ban pas cher