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

  1. Posted May 20, 2011 at 1:15 am | Permalink

    Interesting post but keeping your key in your code like you do is not safe!
    NSString *key = @”a9bk342nziAFD234″;

    It’s very easy to dump all strings used on your binary:
    http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/strings.1.html

    Regards,
    Johann Fradj from the Hot Apps Factory

  2. Kalle
    Posted May 20, 2011 at 4:36 am | Permalink

    Two additional points which helps:

    1) Use SSL for server communications, that makes things more inconvenient for curious minds (and the point of all this is to make the task too inconvenient and/or time consuming for the attacker).

    2) Try to generate the key with an algorithm instead of simply storing it as a string constant in code so that a simple hexdump of your app doesn’t reveal the key.

  3. m0i
    Posted May 20, 2011 at 10:56 am | Permalink

    I don’t have any experience with encryption and data security but I’ve heard a lot about SSL, how is HMAC better?
    And why don’t you use POST instead of GET? It seems to me it would be a more appropriate method, isn’t?

  4. Posted May 20, 2011 at 2:27 pm | Permalink

    Thanks, @Johann. Is there a standard way to use a private key more securely?

  5. Posted May 20, 2011 at 2:33 pm | Permalink

    Hi @m0i and @Kalle,

    I probably should have mentioned SSL in the post. SSL is designed to avoid eavesdropping and tampering with data. It is _not_ designed to prevent someone faking your app. So it is very useful for network security, but it serves a different purpose. Basically, once client A and server B connect via SSL, they have protection against a third person, C, grabbing or messing with their data; this is extremely useful for any private data, such as reading your email, or sending passwords. But it is still up to the server to figure out who it is talking to, and that’s where this HMAC technique is useful.

    Also, @Kalle, thanks for the tips on key generation. I’ll probably add a note to the post once I understand best practices for key generation & storage.

  6. Posted May 24, 2011 at 1:09 pm | Permalink

    For *YEARS* I’ve been trying to figure a way out of doing this and I’ve always come back to the same stumbling pont; the key to encrypt WITH needs to be included in the client.

    Once the key is in the client, the “attacker” can just use that key to “sign” anything they want.

    So how do we stop the attacker getting this key?

    Our options are:
    * Simply make it so damn hard to get it’s not worth it
    * Somehow dynamically generate a key based on a constant user id (still reversible?)

    I’d always been worried about being able to just view the binary and “see” the key… I quite like the idea of generating it using code.

    There is an interesting discussion here:
    http://stackoverflow.com/questions/926172/how-to-hide-strings-in-a-exe-or-a-dll

    Basically it looks like *any* effort to secure the key is just obfuscation. This like (from that stackoverflow url) sums it up perfectly…
    “The moment that you have handed your executable to a user, they have everything they need to find out the secret.”

    Maybe the answer is NOT to hand the user the key in the binary, but have them fetch it manually. Question is, how do you then secure THAT. Encryption would require the binary to be able to decrypt it.

    It’s at this point I run out of idea’s…. Thanks for that tutorial though, it’s certainly helped me understand HMAC!

  7. Posted June 8, 2011 at 2:35 am | Permalink

    Good points, @Nicholas, and thanks for the stackoverflow link. I’ve been doing some research on this question, and coming to similar conclusions. I don’t think it’s possible to completely stop anyone who tries hard enough, but you can make it very difficult for them. I’ll probably add a post in the future focusing on this question.

  8. Anders
    Posted June 15, 2011 at 1:02 am | Permalink

    Is it possible during the initial handshake, the server sends the client a public key?

    The client then enciphers the data using that public key and sends it along to the server, which using a private key, deciphers the data.

    This would stop the client ever having a key stored locally – the server public/private keys can then be changed at any time.

  9. Posted June 16, 2011 at 2:26 pm | Permalink

    Hi @Anders, getting info from the server is an interesting idea. I think it can work if the server can verify the remote device has a legit copy of your app. For example, if I implemented the purchase system, then I could have a trusted server that knows, for every device, all legit apps purchased for that device. (Maybe something more complex for one user owning multiple devices, but skip over that for now.) When a device asks for some secret data, the server will send that secret data exactly once for a legit device, and zero times to a non-legit device. A clever hacker might be able to grab a valid device id before that device gets the secret info, but this would be hard to do. I think this is a better solution that including a private key directly in your app, since malicious users can get a copy of your binary.

    However, I don’t have control over the purchase system, so I can’t do that. I am not aware of any Apple support for this kind of system, although I have looked around for it.

    Next, to move specifically to your suggestion, I would say a public key from the server would not help, since the concern here is fake clients. If the server sends a public key to anyone who asks for it, then a fake client could get that public key and use it just like anyone else. A public key from the server could be used to prevent someone intercepting server-bound data, or posing as the server, but I don’t think it prevents fake clients. You might say,”well then only send the public key to valid clients,” but the whole problem is that there’s no way (that I know of) to know when a client is valid, besides the client having a private key that we build an HMAC out of.

  10. AndersUk
    Posted June 17, 2011 at 9:30 am | Permalink

    Taking my suggestion a step further, it is possible the client sends in the payload of the encrypted message, (using the server public key cipher), a verification key.

    This verification key can be created client side one of two ways: (2 ways will do for now)

    1) using an algorithm that takes the server public key as a seed, and no internally stored keys, generates the verification key. Upon receipt (and deciphering) the server can use the same algorithm path to check the verification key. The bag guys won’t be able to see the in-transit verification key, as it’ll be ensconced within the encrypted message and the only way they could ‘fake one’ would be to disassemble the client side code, and debug it through. (not for the faint hearted)

    2) A more secure way to ensure the client is legit is to have a server side table of registered users. The verification key could be generated from the hash of the users password etc. This way the bad guys would have to get the users login credentials and the algorithm for creating the verification key before they could fake a message to the client. However this requires maintaing a user database on the server side, including the whole register/verify/login/forgot-password functionality. (and then all the Data Protection Act legislation that goes with such things)

    Just a suggestion and food for thought. Thanks for all your efforts BTW.

2 Trackbacks

  1. […] 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. […]

  2. By Bynomial Code » Storing passwords in an app on July 15, 2011 at 3:18 pm

    […] recently covered the topic of sending unfakeable messages from your app to a server, which requires the use of a […]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*