Building secure games and applications

Now that we've discussed some basic things to watch out for, and the things you shouldn't perform in your games; we will now take a look at some simple concepts that we cannot leave out of the game.

Again, most of these concepts apply to web development in general, so those of you coming from that world would feel right at home.

Authoritative server

Hopefully, it is clear by now that the key to having trustworthy information is to ensure that the source of that information is trustworthy. In our case, we rely on the game server to listen to all of the clients and then determine what is the truth about the current game state.

Should you ever find yourself in a situation where you are considering not using a server-client model for your multiplayer game in favor of some alternative format, one thing you should always keep in mind is that the security like that can be obtained by putting an authority between two players. Even if a single player decides to manipulate and cheat his or her own game client, the authoritative game server can ensure that other players still have an equal and fair experience.

While not every game format calls for an authoritative game server, you should have a really good reason for not using one when your specific game could be implemented using one.

Session-based gameplay

One of the benefits of modern browsers is that they feature very powerful JavaScript engines that enable us to do so much in the client with straight JavaScript. As a result, there is a lot of heavy lifting that we can offload from the server to the client.

For example, suppose we want to save the current player's game state. This would include the player's current position, health, lives, score, etc., as well as virtual currency, achievements, and more.

One approach would be to encode all of this information and store it in the user's machine. The problem with this is that the user could alter the saved file, and we would never know about it. Thus, a common step in this process is to create a hash of the final saved file, then later use this same hash to ensure that the game's saved file hasn't been altered.

Note

What is the difference between a hash and an encryption?

Perhaps, you have heard both the terms being used interchangeably, but they're actually very different concepts. While both are often associated with security, this is about the only similarity that they share.

A hash function maps a string of some arbitrary length to a string of some fixed length. Given the same input string, the same output hash is always returned. The main feature of a hash function is that the mapping is unidirectional, which means that the function cannot be reverted in order to get the original input from its output.

For example, the Rodrigo Silveira input string would map to something like 73cade4e8326. Hashing this output string would return something completely different from itself or the original input.

Encryption, on the other hand, is a way to convert some input string into a different representation of that string, but with the ability to reverse (or undo) the function and get back the original input string.

For example, if you use Caesar's cipher (named after the powerful Roman general, not the Brazilian soccer player) to encrypt the Rodrigo Silveira string with a shift value of 3 (which means that every character in the input text is shifted by 3 letters), you'd get the output as Urguljr Vloyhlud.—that is, the third character after the letter R, which is the letter U, and so on. If we apply a shift value of -3 to the output string would give us back the original string.

In short, for all practical purposes, a hash cannot be reversed while an encryption can.

However, if we also store the hash with the client, then all they would need to do after altering the game save file is to recalculate the hash, and we'd be right back where we started.

A better approach would be to calculate the hash on the server, store the hash in the server, and associate it with the player through some user account system. This way, if any tampering is done to the locally stored file, the server can verify it using the hash to which only it had access.

There are also cases when you might want to store API keys or other unique objects of this nature with the client. Again, the key principle here is that anything that touches the client is now under the control of your enemy and cannot be trusted.

Thus, the main takeaway from this section is to always store keys and other sensitive pieces of data like it inside the server and associate and proxy them to and for the player through session tokens.

Security through obscurity

While obscurity is not a form of security, it does add a layer of complexity that slows down the truly determined (and skilled) malicious user and filters out most other evil doers who would otherwise attempt to exploit your game.

In web development, the most common way to obscure your game is by running your final source code through some JavaScript compiler that safely renames variables and function names as well as rewrites code in a way that is equivalent to your original input code but performs the same tasks.

For example, you might have code like the following one, which can be exploited very easily by the player by changing the value of some variables using their browser's JavaScript console:

Gameloop.prototype.update = function(){
    Players.forEach(function(player){
        hero.bullets.filter(function(bullet){
            if (player.intersects(bullet)) {
                player.takeDamage(bullet.power);
                hero.score += bullet.hp;

                return false
            }

            return true;
        });
    });

    // ...
};

We don't have to look too closely at the previous function to realize that only bullets that hit other players in this fictitious game gives damage to each player and increase our own score. Thus, writing a function to replace that is trivial or at least modifying its important parts to the same end can be just as easy.

Now, running that function through a tool such as Google's closure compiler (to know more on closure compiler, refer to https://developers.google.com/closure/compiler/) would output something similar to the following, which is clearly not impossible to manipulate but is certainly not as trivial:

_l.prototype.U=function(){c.forEach(function(e){i.a.filter(
function(o){return e.R(o)?(e.a4(o.d),i.$+=o.F,!1):!0})})};

Most JavaScript obfuscator programs will rename function names, variables, and attributes as well as remove unnecessary whitespace, brackets, and semicolons, making the output program very compact and hard to read. Some additional benefits of using these programs prior to deploying your code include having smaller files that you'll end up sending to your clients (thus saving bandwidth), and in the case of closure compiler, it rewrites parts of your code so that the output is optimal.

The key takeaway from this section is that adding layers of complexity to your code makes it that much more secure, and at the very least, helps you to get rid of some class of attackers. Much like adding a camera above your front door won't necessarily eliminate possible intruders from breaking in, it sure does go a long ways in scaring unwelcome visitors.

"Remember, however, that obscurity is not security at all. It is trivial to deobfuscate an obfuscated JavaScript program (even compiled programs can be easily decompiled back into partial source code). You should never rely on obfuscation and obscurity alone as a solid form of security. Obfuscating your deployed application should be a final touch to an already secure system, especially considering the major benefits of obfuscation, as mentioned previously.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.36.194