How to send messages safely to your server

This post describes a general method of sending messages to a server in a way that prevents hackers from acting like your app. Specifically, I’ll tell you exactly how to add HMAC authentication to your app’s network messages.

If you don’t care about the why and just want to get your hands on some working code, skip ahead to The Code section below.

The Problem

I’m building an iPad game, and my app will be sending high scores to a server. If I built a simple REST interface for adding new high scores, it could be easy for a dedicated player to make up a fake high score. For example, let’s suppose I send an HTTP GET request to this url to tell the server to add a new high score:

http://myserver.com/newHighScore?name=Gilgamesh&score=100

Users can perform packet sniffing on network traffic using tools like Wireshark. Suppose our player, Gilgamesh, really really wants to have the all-time high score. If he sees this protocol, he could just open his favorite browser and type in a url like this:

http://myserver.com/newHighScore?name=Gilgamesh&score=3240923409

At this point, there’s not much my server can do. I can look at the user agent string, but that is also easy to fake. I could add a sort of password as an extra parameter:

http://myserver.com/newHighScore?name=Gilgamesh&score=100&password=supersecret

but this is still easy to crack with packet sniffing.

So how do you prevent malicious users from sending fake messages as if they were your app?

The Solution

I’m glad you asked! Because, luckily, there is a whole subfield of cryptography dedicated to this question. If you’re curious about diving into this general subject, I suggest you start with the wikipedia page on message authentication codes. If you just want to get your iPhone app working securely, read on:

The technique this code uses is called HMAC, which stands for hash-based message authentication code.

This is how it works: I have a message, which is a string. I’m not going to encrypt it, but I want to send it in an unfakeable way. To do this, I’ll compute a hash of the message, along with a private key (another string), and send that along with the message. If you’re thinking of hash tables, then, yes, this is sort-of halfway similar, but not exactly. I am using a hash function which takes a possibly-complex object and gives me a simpler object as output. For hash tables, we want the output to be “randomly distributed” in a certain sense. For use as an HMAC, we want a hash function to be irreversible. The point is that, if someone knows the output of your hash function, it’s very hard for them to compute the input.

And there are a bunch of hash functions designed with that goal in mind. Some of them are called MD5 (“message digest 5”), SHA1 (“secure hash algorithm”), and SHA2, which is actually a set of hash functions. Check out wikipedia’s MD5 pseudocode to get an idea of how hard it might be to reverse the output of one of these guys. (This page is also fun.)

One thing I want to point out is that malicious users can fake a message identical to one you’re already sent. If your API is idempotent (i.e., sending the same request twice has no additional effect), then this is fine. Otherwise, you will want to add some new piece to the message, making it harder to fake. For example, the client could receive an additional string from the server, called a nonce, and have to include that in the hash input. If your nonces are always different, a malicious use could no longer fake the same message.

The Code

I’ve added a small NSString category to the moriarty library to provide HMAC strings. This is how you can use it:

#import "NSString+HMAC.h"

// Within a method in your view controller:
NSString *msg = [self createMessage];
// This is where your private key goes.  Don't just copy this one!!  That would not be secure.
NSString *key = @"a9bk342nziAFD234";
NSString *hmac = [msg hmacWithKey:key];
NSString *urlStr = [NSString stringWithFormat:
                    @"http://%@/%@?msg=%@&hmac=%@",
                    domain, path, msg, hmac];
NSString *reply = [self pingUrl:urlStr];

If you need to send complex data, I suggest using JSON along with Stig Brautaset’s JSON framework library. You can have an NSDictionary or NSArray of strings, numbers, and other arrays/dictionaries, and easily encode this as an NSString by calling [myObj JSONRepresentation];.

What does the server do with this? If you’re using php, here’s how to verify the authentication:

function hmacMatchesMessage($client_hash, $msg) {
  $key = 'a9bk342nziAFD234';  // Should match client's key.
  $server_hash = hash_hmac('sha256', $msg, $key);
  return strcmp($client_hash, $server_hash) == 0;
}

if (!hmacMatchesMessage($_GET['hmac'], $_GET['msg'])) exit();
// Instead of exit(), you could print an error if you're feeling friendly.
$msgObject = json_decode($_GET['msg']);  // If you sent a JSON string.

You can get NSString+HMAC.{h,m} by downloading moriarty; free to use, Apache 2 license.

Or, you can just copy the code from here. As you can see in the m file, this is basically a convenience wrapper around a library that is included by default in iOS (but is difficult to learn how to use).

May your messages be safely authenticated!

//
//  NSString+HMAC.h
//
//  Created by Tyler Neylon on 5/19/11.
//
//  Methods for sending authenticated messages; uses SHA256.
//  See here for an overview of the purpose of these methods:
//  http://en.wikipedia.org/wiki/HMAC
//
//  Sample usage:
//    NSString *msg = [self createMessage];
//    NSString *key = @"a9bk342nziAFD234";  // Private key.
//    NSString *hmac = [msg hmacWithKey:key];
//    NSString *urlStr = [NSString stringWithFormat:
//                        @"http://%@/%@?msg=%@&hmac=%@",
//                        domain, path, msg, hmac];
//  Now send a request to urlStr; if the server knows
//  the private key, it can recompute the HMAC string, and if they
//  match, have confidence that the message originated from someone
//  else who knows the private key as well.
//

#import <Foundation/Foundation.h>

@interface NSData (Hexit)

- (NSString *)hexString;

@end

@interface NSString (HMAC)

- (NSString *)hexString;
- (NSString *)hmacWithKey:(NSString *)key;

@end
//
//  NSString+HMAC.m
//
//  Created by Tyler Neylon on 5/19/11.
//

#import "NSString+HMAC.h"

#import <CommonCrypto/CommonHMAC.h>

@implementation NSData (Hexit)

- (NSString *)hexString {
  NSMutableString *str = [NSMutableString stringWithCapacity:[self length]];
  const unsigned char *byte = [self bytes];
  const unsigned char *endByte = byte + [self length];
  for (; byte != endByte; ++byte) [str appendFormat:@"%02x", *byte];
  return str;
}

@end

@implementation NSString (HMAC)

- (NSString *)hexString {
  const char *cStr = [self UTF8String];
  return [[NSData dataWithBytes:cStr length:strlen(cStr)] hexString];
}

- (NSString *)hmacWithKey:(NSString *)key {
  const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
  const char *cData = [self cStringUsingEncoding:NSASCIIStringEncoding];
  unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
  CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
  return [[NSData dataWithBytes:cHMAC length:sizeof(cHMAC)] hexString];
}

@end

10 Comments