Encrypt your OAuth cookies – PHP Example

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

Github repository

PHP for Windows – the Mcrypt library seems to come with this installation. Mac users may have to install MAMP.

PHP Manual for Mcrypt

OAuth Community

[Modified: April 27, 2013 – added link to github repo.]

Changing your Twitter password – Bad design or security hole?

When you reset your Twitter password it does not automatically cascade across all of your allowed applications. In other words, ANY Twitter app will continue to function just fine until you manually revoke permissions. Yep…manually. The apps even continue to work after you, say, reboot your phone. However, once you’ve revoked permissions then most apps will prompt you for a new password. More on that at the end of this post.

Why should you care? Because if your Twitter password or your smartphone is ever stolen and you don’t revoke permissions, then the bad guys can continue to use Twitter apps that you approved.

Twitter doesn’t really tell you much about how password changes work. On the password changed acknowledgement page there’s a deceptively mild mannered blurb of text that simply asks if you want to review the applications that can access your account. This is what the page looks like:

Call me crazy, but it seems natural to me that if you change your Global password, that any application using that password should be auto-majically changed as well. The technical answer is contained deep within the Twitter Developer FAQ:

When using OAuth, application connectivity and permissions do not change when a user resets their password on twitter.com. The relationship between Twitter, a user, and a third-party application do not involve a username and password combination. When a Twitter user changes their password, we’ll now ask the user whether they would also like to revoke any of their application authorizations, but any revocations are manually executed by the end user.

My response to this is your average user has no clue about the pros and cons of using OAuth and they most likely don’t really care.

My immediate suggestion to Twitter is that they should provide a “Learn More” link on the password has been changed acknowledgement page that provides informative bullet points on how changing your password affects or doesn’t affect other Twitter applications. They should also include a warning in bold letters that tells you that applications can and will continue to use the old password — until you revoke their access.

Now, full disclosure is that I did get my password stolen and I was, fortunately, able to quickly reset it before any major damage happened. I was lucky. However, it wasn’t until a day later that I had done enough research to know about revoking access on all of my applications. Like I said above: I figured the password change would automatically affect all applications. However, I simply got curious when I noticed my Android Twitter app kept working fine and never asked me for a new password. It was possible the criminals could have still used any of these other applications with my old password. Yikes!

Conclusion. My experience after revoking permissions wasn’t exactly seamless and your mileage may vary (YMMV) depending on how your third party app was built. My Android Twitter application simply appeared to send a tweet with no indication anything was wrong. I only knew it wasn’t sending tweets because I checked using Twitter’s web app. Only after I did a full restart on the phone did the app finally ask for a password. Hmmm.  TweetDeck at least let me know that my tweets failed to send, and within a minute it displayed a dialog box asking for the new password (TweetDeck screenshot below). Also, it’s important to note that after entering the new password Twitter mysteriously un-revoked my access and I could send tweets again.

The good news is that revoking access does immediately shut off an apps ability to send tweets. The so-so news is that once the correct new password was entered, then Twitter mysteriously un-revokes status on the app. This bothered me. I would think if you manually revoke access to an app, then as a security best practice you would also have to manually un-revoke access as well.

So, is this poor design, a security hole or maybe even both?

Resources:

Twitter Developer FAQ – How do password resets affect authorization?

Twitter Application Permissions Model

[Edited 8/26/12] Twitter Help – My account has been compromised

11 Twitter OAuth Tips and Tricks

If you are building a Twitter app then at some point you are going to be faced with integrating OAuth. This article summarizes my top eleven time saving tips-n-tricks. If it helps you out or if you have other tips and tricks please share your comments and ideas.

Okay so what’s OAuth? Bottom line, it’s an open protocol that gives your app special access on a user’s behalf without having to deal with storing their user name and password.  So, rather than usernames and passwords, you are going to be handling tokens. You may already be thinking whoa! Aren’t I just trading one security problem with another? Sure, there are security issues with OAuth and if you search on the internet you’ll find plenty of rants about them. The bottom line is that tokens, like usernames and passwords, can also be intercepted and potentially used to access someone’s account. I address that issue below. But, if you want to build a Twitter app you are going to have to figure out the best way to implement OAuth, end of story.

Read on and let me know what you think:

  1. Before trying to build an Oauth library from scratch, check out ones that have already been built. It will save you a ton of time. For starters, check out this page: https://dev.twitter.com/pages/oauth_libraries
  2. Read Twitter’s article on “Authenticating requests with OAuth.”  It’s not very long and I thought it provided a lot of very helpful information.
  3. Register your app at https://dev.twitter.com. If you don’t register, then OAuth transactions will not work.
  4. When you register your app with Twitter, on the settings page make sure the Callback URL points to an actual, functioning, domain name whether you will use it or not. It should look something like this: https://mydomainname.com/oauth.php. You’ll quickly find out you can’t specify localhost, or if you do it doesn’t work. More on that in a minute.
  5. If you are building a web-based Twitter app you will need to run all your requests through a proxy. In your proxy, you can override Twitter’s Callback URL parameter by setting your signature’s oauth_callback parameter (see reference section below for more info). This is especially useful for prototyping and testing when you want to use something like https://localhost/oauth/confirm.php. If don’t override the oauth_callback parameter, then you can’t use localhost for testing.
  6. Never ever store your Twitter consumer key or consumer secret on the client. Client apps can be de-compiled and then your secret won’t be so secret anymore.  Store these on your proxy.
  7. Always encrypt oauth_token and oauth_token_secret when storing them on a client app.
  8. Use HTTPS for all oauth transactions that require a user name and password. Otherwise, this information can easily be intercepted, especially over public wi-fi.
  9. Twitter avatar images also require the use of a proxy. Twitter has implemented fully restrictive cross-domain polices so that web browsers can’t access these images directly.  If you are getting cross-domain errors then check out this post for hints. 
  10. Twitter’s OAuth access tokens don’t expire, although for security reasons it’s a good idea to refresh them on a regular basis. Just remember, refreshing them does involve forcing the user to re-login with Twitter.
  11. Twitter has a very handy online tool, called the Twurl Console, for testing out API calls via HTTP and seeing the headers and results without having to write any code. Once you have registered your application, you can find the tool here: https://dev.twitter.com/console You can also use something like Firebug or Fiddler, but they don’t have all the twitter API commands built into a pulldown list!

Just a few references:

To see a working OAuth sample app along with Flex source code and PHP proxy go here.

Oauth 1 Website: https://oauth.net/

Latest OAuth spec RFC5849: https://tools.ietf.org/html/rfc5849

Reference to the OAuth call_back parameter: See section 2.1 of RFC5849 above. To see the call_back implemention in the code sample above, look in the file EpiOAuth.php.