03 Aug / 2010

iPhone dev : Securely Handling UDID

Each iPhone and iPad has a unique device ID (just like MAC address) that
uniquely identifies that device.  This UDID as it is called, is
used by many iPhone programs.  Some examples could be

  • Game Score keeping services : they use your UDID to keep track of
    your score & rank
  • storing some user data on the server : for example a RSS reader
    could store your favorite blogs on a server as a backup
  • used for analytics : Companies like Flurry provide mobile
    analytics, just like Google Analytics for web sites.  They use
    UDID to identify the device

With so many handy uses, lot of client-server iPhone apps use the
UDID.  Even though it is not as sensitive as a phone number, I
feel, it should be treated as ‘some what sensitive data’.  So lets
see how we can handle the UDID securely.

Transmitting UDID across the Internet securely

Avoid sending UDID over plain HTTP.  This enables anybody to
intercept and snoop the package.   Instead use HTTPS, also
known as SSL (Secure Socket Layer).  So what is involved in switching from http to https?

  • Server side : You need to
    obtain and install an SSL certificate.  Lot of  ‘domain’
    companies like GoDaddy, Dotster ..etc offer affordable
    certificates.  They cost around $30 / year.

    • get a cert
    • install following this guide
    • check that your certificate is installed properly using this online tool 
  • Client Side : If setting
    up the server-side was a little involved… don’t worry.
    Switching to HTTPS on the iPhone is super easy.    iPhone
    SDK’s network library has built in support for SSL. SO all you have to
    do is change your server URL.change the original url :

    http://mobile.myserver.com
    

    to   :

    https://mobile.myserver.com

    that is it!

Now all the data you are posting from iPhone, including UDID, are
transmitted securely!

Here is a quick objective-C snippet to send UDID and a another sample parameter to the server

// parameters to send
NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];
int article_id = 10; // some article id

NSString *serverURLStr = @"https://mobile.myserver.com/read_article";
// prepare a GET request
NSMutableString *postString = [[NSMutableString alloc] init];
[postString appendFormat:@"%@=%@&", @"udid", udid];
[postString appendFormat:@"%@=%@&", @"article_id", [NSString stringWithFormat:@"%d", article_id]];	
NSString *postString2 = [postString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString *urlString = [NSString stringWithFormat:@"%@?%@", serverURLStr , postString2];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request  = [NSURLRequest requestWithURL:url];
NSError *error = nil;
NSURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

// release..
[postString release];
[urlData release];

Handling UDID at the Server side

So now we got UDID securely from the iPhone app, we need to make sure
we handle it securely on server as well.  I will use Ruby on Rails
based web application as our server side application.

Stop LOGGING UDID

Rails logs each requests with parameters.  So a sample log look
like:

Processing MobileController#read_article (for 127.0.0.1 at 2010-08-03 11:57:25) [GET]
  Parameters: {"udid"=>"ABC-123-XYZ", "action"=>"info", "controller"=>"mobile",  "article_id" => 23}

Lets disable logging udid.  Lets add ‘filter_parameter’  to
our controller.

 class MobileController < ActionController::Base
  filter_parameter_logging :udid #, :another_param

This will filter-out logging UDID.  Here is a sample output

 Processing MobileController#read_article (for 127.0.0.1 at 2010-08-03 11:57:25) [GET]
   Parameters: {"udid"=>"[FILTERED]", "action"=>"info", "controller"=>"mobile",  "article_id" => 23}

Bingo! See how the UDID is no longer printed out…and UDIDs won’t bleed all over our logs

Securely Storing UDIDs

If we are transmitting UDID, most likely we are storing them in a database for later analysis, data mining ..etc.  Since we are transmitting UDID securely, lets also make sure we store them securely.

Here is a sample Ruby on Rails data model migration file

class CreateUserLogs < ActiveRecord::Migration
  def self.up
    create_table :user_logs do |t|
      t.string :udid
      t.integer :article_id

	  #other fields
	  #       t.string :ip_address
	  
      t.timestamps
    end
	
	# add index for faster lookups
    add_index :user_logs, :udid
    add_index :user_logs, :article_id
  end

  def self.down
    drop_table :user_logs
  end
end

Approach 1 : Store plain UDID (Don’t Do this!)

def log(params)
  userlog = UserLog.new
  userlog.udid = params[:udid]
  userlog.article_id = params[:article_id]
  userlog.save!
end

Here we are storing udid as is, in plain text.  Lets secure it.

Approach 2 : Secure Hash

A simple way to secure data is ‘one way hashing’ it.   The idea is
‘secret’ —– hash function —-> encrypted-secret
encrypting is pretty fast.  But guessing the ‘secret’ from ‘encrypted-secret’ is difficult

So lets re-write our method as follows

def log(params)
  userlog = UserLog.new
  userlog.udid = hash_string(params[:udid])
  userlog.article_id = params[:article_id]
  userlog.save!
end

good!  We are calling ‘hash_string’ function  (we yet have to write it) to encrypt our UDID.  The stored UDID is now encrypted & safe!

Which Hash?

There are a number of encryption hash choices.  Wikipedia lists the choices here : http://en.wikipedia.org/wiki/Cryptographic_hash_function

The hash function should take a string and return back a string.  The hashed value should be an ‘ascii string’ rather than binary, makes it easier to store it in database.

MD5 has been a popular choice.  It hashes the data into a 32-char.
Lets test this, kick off ‘irb’  command line ruby  or ‘script/console’ in a rails app

irb
>> require 'digest'
=> true
>>  Digest::MD5.hexdigest("ABC-123-UDID")
=> "9f358e9002249e661d8a6275ff0044a4"

We got our hash.  There are better hashing algorithms than MD5, namely SHA.

SHA1 / SHA128

>>  Digest::SHA1.hexdigest("ABC-123-UDID")
=> "1f77aba76084eaaf4f86e63f7138d80ba35e2a01"

this gives us 40-char hash.

SHA256  (64-char hash)

>> Digest::SHA256.hexdigest("ABC-123-UDID")
=> "7ef304bf29632978ad80d0bd79443f7296ffdd9fefacbbdd9e0400cce4cc312e"

 

SHA386 (96 chars)

>>  Digest::SHA384.hexdigest("ABC-123-UDID")
=> "406b1a42c73d200cfb3912371b1f7223c747939a4f1af7b726ac0d4eb19d2107ae02de17db0ad342e99628a23afd4392"

SHA-512 (128-chars)

>>  Digest::SHA512.hexdigest("ABC-123-UDID")
=> "1009141d52006b9ab3484f335afa476890271cfdb11f6e1ac0a88f33353c942c2e43e697daef69be8a81b41eca90bf029ccdf15f68849ecc66cbd6b3d5037623"

As we use higher-and-higher SHA levels, the encrypted string grows longer (32, 40, 64, 96, 128 chars) making them more and more secure.  So pick one that suits your security needs.  SHA256 is usually plenty.

So here is our hash function

def hash_string(str)
  Digest::SHA256.hexdigest(str)
end

Lets add a little Salt

Now that we have a pretty good hash function (SHA256) we feel pretty good, right?  Well almost.  Even though doing reverse-coding from hashed string is ‘hard’, it is a relative term.  The technique is called ‘Rainbow Tables’.  Read more here:

http://en.wikipedia.org/wiki/Rainbow_table

http://www.codinghorror.com/blog/2007/09/rainbow-hash-cracking.html

So we shouldn’t just plainly encrypt  just UDID.  To thwart rainbow attacks,  we can add a little SALT.   SALT is just a random string we come up with,  so our encrypted value can’t be looked up in rainbow tables easily.  So lets update our hash function

@@salt = "w329*&lkj32xnb"  #just any random string
def hash_string(str)
  Digest::SHA256.hexdigest(str  + @@salt)
end

Now we are home!

DO NOT change the SALT going forward.  This will  change how UDIDs are encrypted.

So there we go.  We have covered how to handle UDID securely from iPhone app and also server.

Further Thoughts

If you are skittish about sending UDID over HTTPS,  you can actually encrypt the UDID with one of these algorithms at the iPhone app before sending it to the server.  CommonCrypto library can be used for encryption.

Sujee Maniyam
Sujee is a founder, principal at Elephant Scale where he provides consulting and training on Big Data technologies

1 Comment:



Leave a Reply



Copyright 2015 Sujee Maniyam (