This post only serves as a reminder that if you decide to use OAuth cookies you should make sure that they are adequately encrypted. I’ve seen a number of web-based OAuth applications recently that left their cookies unencrypted and I wanted to provide a fully working example.
Why should you care? Any crook with a little bit of knowledge on how to generate header files can use oauth_token and oauth_token_secret and take control of your customers account.
I don’t know why but several of the most popular PHP OAuth libraries don’t mention this as a best practice. So I’m making an effort to shout out that encrypting your cookies is a very good thing to do and it involves the following:
- Encrypt oath_token and oauth_token_secret
- Write encrypted values to cookie
- Read encrypted values when required
- Provide functionality to delete cookie
Another gotcha is if you allow someone to log out of your app without deleting the cookie, then it’s possible someone else could come along and get the cookie then use it to generate requests on behalf of your client application.
So here’s a fully working PHP example of how to encrypt cookies. You can also access this project through a github repository.
Step 1: generate your key and initialization vector:
<?php session_start(); require_once('config.php'); require_once('Encrypt.php'); header('Content-Type: application/json'); $test = new Encrypt(null,null,DEFAULT_TIME_ZONE); echo "\n\n---------\n\nGenerate Key (Base64): " . $test->get_RandomKey(); echo "\n\n---------\n\nGenerate IV (Base 64): " . $test->get_IV(); ?>
Step 2: setup the config file:
<?php /** * @file * A single location to store configuration. */ //REQUIRED - Encrypt your cookies //https://si0.twimg.com/images/dev/oauth_diagram.png //Create your own unique ENCRYPTION_KEY via Encrypt.get_RandomKey() define('ENCRYPTION_KEY','Q83dBef2tgmHKZ9T1htFA2Y+jZgdler0szN28rjBf8o='); //Create your own unique initialization vector via Encrypt.get_IV() define('IV','C2Oez0DLMQ8rCcgYFJwzCw=='); define('DEFAULT_TIME_ZONE','America/Los_Angeles'); ?>
Step 3: test ability to set an encrypted cookie and decrypt it.
<?php session_start(); require_once('config.php'); require_once('Encrypt.php'); $key = base64_decode(ENCRYPTION_KEY); $iv = base64_decode(IV); echo "Key: " . $key; echo "\n\nIV: ". $iv; $test = new Encrypt($key,$iv,DEFAULT_TIME_ZONE); if(isset($_COOKIE["twitterapp"])) { header('Content-Type: application/json'); $retrieved_cookie = $_COOKIE["twitterapp"]; echo "\n\nEncrypted value: " . $retrieved_cookie; echo "\n\nActual Value: " . "VodG7slxk+w0INvK66gztp4TOLijNzyiWDzI8Z4IU4PTiBJJkRPdkaEbDtXFYUVkCVU="; echo "\n\nDecrypted Value: " . $test->decrypt(base64_decode($retrieved_cookie)); } else { $fake_cookie_string = "VodG7slxk+w0INvK66gztp4TOLijNzyiWDzI8Z4IU4PTiBJJkRPdkaEbDtXFYUVkCVU="; $encrypted_cookie = base64_encode($test->encrypt($fake_cookie_string)); setcookie("twitterapp", $encrypted_cookie, $cookie_life, '/', OAUTH_COOKIE_DOMAIN); header('Location: https://your_domain_name/oauthphp/test.php', true, 302); exit; } ?>
And, here is the Encrypt Class:
<?php class Encrypt{ /* * Basic encryption Class * Version: 1.0 * Author: Andy Gup */ private $key; private $iv; function __construct($encryption_key,$iv,$time_zone) { $this->key = $encryption_key; $this->iv = $iv; date_default_timezone_set($time_zone); } public function encrypt($value){ $final = null; if($value == null || $this->key == null || $this->iv == null) { header("HTTP/1.0 400 Error"); echo "\n\n\nERROR: Null value detected: check your inputs"; exit; } else { try { $final = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, $value, MCRYPT_MODE_CFB,$this->iv); } catch(Exception $e) { header("HTTP/1.0 400 Error"); echo "\n\n\nERROR: Failed encryption. " .$e->getMessage(); exit; } } return $final; } public function decrypt($value){ return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, $value, MCRYPT_MODE_CFB,$this->iv); } public function get_RandomKey(){ $result = openssl_random_pseudo_bytes(32, $secure); return base64_encode($result); } public function get_IV(){ $size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CFB); return base64_encode(mcrypt_create_iv($size, MCRYPT_RAND)); } } ?>
Resources
PHP for Windows – the Mcrypt library seems to come with this installation. Mac users may have to install MAMP.
[Modified: April 27, 2013 – added link to github repo.]