Facebook and Unity Tutorial – Part Three

A note before you start: This is a Unity tutorial about implementing Facebook in Unity projects. It’s a technical how-to guide for Unity developers who want to use Facebook Connect in their web-apps. If that sounds interesting, read on! If it does not, you should check this out. And stay tuned for other blog posts. 😉

The Facebook game we built in part one and part two works fairly well at the moment, although it’s not very exciting. Facebook profile pictures are visible, and we can store and retrieve highscores successfully. However we also saw Mark Zuckerberg having a very suspicious highscore by rolling 7 with a 6 sided dice. In this part we’ll learn how to modify our own scores, and prevent malicious users from doing so.

But first a disclaimer: Creating a 100% secure application is not possible. Just look at any major game released the last several years, they all include some copy protection but its just a matter of time before they are hacked. Hackers are a very creative, and they will always find new innovative ways to exploit your program and make your life miserable.  So in a way, the only thing you are doing is making it harder for malicious users to cheat in your game, completely preventing it now and in the future is sadly impossible.

Hacking your own game

To understand security you have to step towards the dark side too, and learn how to ‘hack’ your application. We’ll focus on one way how Mark Zuckerberg could have modified his score in this tutorial. There are other subjects of security such as copy protection that arent covered.

Webdevelopers have several tools available to them to debug and test their applications, such as Firebug and the built in Chrome Developer Tools. These tools are vital to a webdeveloper, as they allow insights to websites a normal user wont see. However we, a malicious user, could also use these tools to inspect how an application works and modify its execution, such as modifying scores. I’ll be using Firebug in the following samples, but any other web-debugger will do.

inspect

Start Firebug and go to the net tab. This will show any external calls being made, and resources being loaded. If you load the page with the net tab open you’ll see the Unity3d file being loaded, facebook images being requested and even our own webservice being called. Anyone can inspect this and find out where your webservice is located and how you use it. Try it out, play the game with the net tab open and you’ll see the insert_highscore request being made. Malicious users could visit the URL manually, supplying their Facebook id and any score they desire like what we did to test our service!

We need a way to verify that what we send from unity and receive in our backend is untouched and exactly the same. Again, 100% protection is impossible, but we can at least make it harder for the attacker. The way we verify the request is by one-way-hashing the parameters we send, and validating the hash serverside. One way hashing means using a algorithm like MD5 or SHA1 to convert one string in another, but this process is not reversible. For example; the MD5 hash of ‘Monkey’ is “4126964b770eb1e2f3ac01ff7f4fd942“, but ‘Donkey’ will turn into “046d93852f6419ac27a74b4ebcaa32e4“. Eventhough the original is very similar their hash is completely different. We can use this to our advantage by calculating the hash of the data we send, send that along the original data to the server and then compare the hash we got with the one we generate again at the server. For example:

  1. Append the Facebook ID and score, use a seperation character and append another secret key for added security. For example: 100000229612727_4_paladinrules.
  2. Hash the appended data and send it along with the original data. Our sample hash would be 3b707fdc8d8f54c27a543b16a68efcf7.
  3. On the server do the same process, append the data we recieve and our secret key. Verify the hashes are the same.

Now if the evil Mark would attempt to change his score the hashes wont match, and we can reject the request completely. Enough talk; lets implement our theory!

Modify insert_highscore.php to add hashing:

[sourcecode language=”php”]
<!–?php <br ?–> mysql_connect(‘localhost’,’root’,”); //Setup database connection
mysql_select_db(‘highscores’);
$score = @$_GET[‘score’];
$fbid = @$_GET[‘fbid’];
$request_hash = @$_GET[‘hash’];

if(!$score || !$fbid || !$request_hash ) //Make sure we get all input
die("No fbid, score or hash");

$data = $fbid . "_" . $score . "_paladinrules"; //Hash it up using our format fbid_score_paladinrules
$hash = md5($data); //Hash it up!
if(strtolower($hash) != strtolower($request_hash)) //The hashes are different, the request is invalid.
{
echo "Invalid request!";
} else { //it seems legit, lets insert it!
mysql_query("INSERT INTO scores (fbid, score) VALUES (‘$fbid’, $score)");
} ?>
[/sourcecode]

As you can see, we now also expect another GET parameter called `Hash` to be passed and compare that to the hash we calculate. When it matches we happily trust the input and insert it, otherwise we show a message to scare off any attackers. Hashes should be case insensitive because some implementations of MD5 capitalize the output and others dont.

Modify DiceGame’s submitHighscore to include the hash:

[sourcecode language=”csharp”]
IEnumerator submitHighscore(int score, string fbid)
{
string hash = md5(fbid + "_" + score + "_paladinrules");
WWW webRequest = new WWW("http://localhost/insert_highscore.php?score=" + score + "&fbid=" + fbid + "&hash="+ hash);
yield return webRequest;

yield return retrieveHighscores(); //After we submit we’d want an update, perhaps someone else played too!
}
private string md5(string input)
{
// step 1, calculate MD5 hash from input
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hash = md5.ComputeHash(inputBytes);

// step 2, convert byte array to hex string
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
}
[/sourcecode]

It is very similar, albeit MD5 is a bit more tricky but luckily C# is well documented. We hash the parameters using the format we’ve made up, and send it to the server.

Build your game and play it, then try to alter your score as you’ve done before. You’ll see the hashes wont be the same, and your score will be rejected. This approach will keep most script-kiddies out, however its only a matter of time before a malicious user figures your secret format out and would be able to generate the hashes themselves. You could further complicate your security model by for example adding another secret key which is user specific, or using a different hashing algorithm.

Even though we are now secure from some basic forms of attacks, we cant rejoice yet. There are plenty of others ways to hack and cheat in your application, and there are always new ways being explored too. You should be especially scared about SQL Injection, which is the number one most commonly used attack on websites. Although theoretically if you cant alter the values you send to the server, you’d rather be safe then sorry.

This concludes the tutorial about Facebook and Unity. I hope you enjoyed it! Please let us know what you think in the comments or the Unity forum topic.

Tijmen van den Heuvel
Paladin Studios

7 Comments

Post A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.