Don’t hash secrets

Written on 21 September 2014, 04:10pm

Tagged with: , , ,

Don’t hash secrets, HMAC them!
This is an old but incredibly useful article written by Ben Adida, currently director of engineering at Square, previously working at Mozilla.
The idea is that simply hashing + salting the secrets is not enough. You need to HMAC them (Hash-function Message Authentication Code). HMAC always uses a hashing function (like MD5, SHA1, etc), but this hashing function is not used to hash the secret alone.

If you know SHA1(secret || message), then you can compute SHA1(secret || message || ANYTHING)
You don’t need to know exactly how HMAC works, just like you don’t need to know exactly how SHA1 works. Under the hood, what’s approximately going on is two hashes, one after the other, with the secret combined after the first hash.
Don’t hash secrets, HMAC them!

PHP has the hash_hmac function starting with the version 5.1.2. Interesting enough, CakePHP’s default authentication mechanism only uses hashing+salt, not HMAC. And MediaWiki uses something closer to HMAC.

But there’s a catch. If the secret is longer than the block size of the hash function, then HMAC will use the hash of the secret. See here and here (screenshots below)

custom_hmac
hmac2

And this leads to the following problem:

Mathias Bynens elaborates this:

SHA-1 has a block size of 512 bits, which equals 64 bytes.

So in this case, if the supplied key takes up more than 64 bytes, then SHA1(key) is used as the key. More generally, for any chosen_password larger than 64 bytes, the following holds true (pseudo-code):

PBKDF2_HMAC_SHA1(chosen_password) == PBKDF2_HMAC_SHA1(HEX_TO_STRING(SHA1(chosen_password))

PBKDF2+HMAC hash collisions explained

Takeaway? Don’t hash secrets, HMAC them. But make sure that the length of the secret is not larger than the block size of the hashing algorithm.
On the same line, maybe using passwords longer than 64 bytes is not such a good idea… 🙂

iStock_000020861023Small
Photo: istockphoto

Cronjobs in CakePHP 2.*.* in 5 steps

Written on 30 August 2012, 07:34pm

Tagged with: , ,

Just adapting a solution from 2006 to the 2012 version of CakePHP 🙂
Calling controller actions from cron and the command line

Step 1: copy app/webroot/index.php to app/cron.php

Step 2: edit app/cron.php
Change the last 3 lines of code as follows:

//---------THESE LINES:
	App::uses('Dispatcher', 'Routing');

	$Dispatcher = new Dispatcher();
	$Dispatcher->dispatch(new CakeRequest(), 
		new CakeResponse(array('charset' => Configure::read('App.encoding'))));

//-----------CHANGE TO:
	App::uses('Dispatcher', 'Routing');

	define('CRON_DISPATCHER',true); 

	if($argc == 2) { 
		$Dispatcher = new Dispatcher();
		$Dispatcher->dispatch(new CakeRequest($argv[1]), 
			new CakeResponse(array('charset' => Configure::read('App.encoding'))));
	}

Step 3: create Controller/CronController.php

 class CronController extends AppController {

	public function beforeFilter() {
	    parent::beforeFilter();
	    $this->layout=null;
	}

	public function test() {
		// Check the action is being invoked by the cron dispatcher 
		if (!defined('CRON_DISPATCHER')) { $this->redirect('/'); exit(); } 

		//no view
		$this->autoRender = false;

		//do stuff...

		return;
	}
}

Step 4: Run your cron using the command line:

# php ./app/cron.php /cron/test

Step 5: Test that loading the same script in browser is not allowed (for security reasons):
http://yourdomain/cron/test redirects to http://yourdomain/

Multiple languages in a CakePHP 2.* application in 5 steps

Written on 1 August 2012, 10:21pm

Tagged with: , ,

Below is a solution that I implemented in a CakePHP project needing internationalization (i18n).
It involves showing the language in the URL (so the URLs will look like app.com/:lang/:controller/:action/...) and storing it in the Session + Cookies.
It is basically the implementation described on http://nuts-and-bolts-of-cakephp.com. I only added some minor changes to make it work under CakePHP 2.* (the solution in the link above was implemented in November 2008, for CakePHP 1.3).
The implementation below was tested on CakePHP 2.2 (1 August 2012).
Feel free to add your comments/questions in the form below.

Comments inline:


// Step 1: app/Config/routes.php
Router::connect('/:language/:controller/:action/*',
                       array(),
                       array('language' => '[a-z]{3}'));

//Step 2: app/Config/core.php
Configure::write('Config.language', 'eng');

//Step 3: create app/View/Helper/MyHtmlHelper.php
App::uses('HtmlHelper', 'View/Helper');
class MyHtmlHelper extends HtmlHelper {
	public function url($url = null, $full = false) {
        if(!isset($url['language']) && isset($this->params['language'])) {
          $url['language'] = $this->params['language'];
        }
        return parent::url($url, $full);
   }
}

//Step 4: app/Controller/AppController.php
class AppController extends Controller {
	public $components = array('Cookie','Session');
	//set an alias for the newly created helper: Html<->MyHtml
	public $helpers = array('Html' => array('className' => 'MyHtml'));

	public function beforeFilter() {
          $this->_setLanguage();
        }

	private function _setLanguage() {
	//if the cookie was previously set, and Config.language has not been set
	//write the Config.language with the value from the Cookie
	    if ($this->Cookie->read('lang') && !$this->Session->check('Config.language')) {
	        $this->Session->write('Config.language', $this->Cookie->read('lang'));
	    } 
	    //if the user clicked the language URL 
	    else if ( 	isset($this->params['language']) && 
		($this->params['language'] !=  $this->Session->read('Config.language'))
	    		) {
	    	//then update the value in Session and the one in Cookie
	        $this->Session->write('Config.language', $this->params['language']);
	        $this->Cookie->write('lang', $this->params['language'], false, '20 days');
	    }
	}

	//override redirect
	public function redirect( $url, $status = NULL, $exit = true ) {
		if (!isset($url['language']) && $this->Session->check('Config.language')) {
			$url['language'] = $this->Session->read('Config.language');
		}
		parent::redirect($url,$status,$exit);
	}
}

//add the links to the languages:
//Step 5: app/View/...
echo $this->Html->link('English', array('language'=>'eng')); 
echo $this->Html->link('Français', array('language'=>'fre')); 

Update August 3rd 2012: Added ‘private’ visibility for the _setLanguage function
Update September 28th 2012: Modified the first step (app/Config/routes.php), adding more restrictive rules for the languages. Before, if the user somehow loaded an URL like /css/foo/bar – then ‘css’ was incorrectly considered a language.


	Router::connect('/:language/:controller/:action/*',
                       array(),
                       array('language' => 'eng|fre'));

	Router::connect('/:language/:controller',
                       array('action' => 'index'),
                       array('language' => 'eng|fre'));	

	Router::connect('/:language',
                       array('controller' => 'welcome', 'action' => 'index'),
                       array('language' => 'eng|fre'));