js:void(0);

Common Pitfalls in JS-based Games

• javascript, and incremental

Welcome! You might be reading this out of curiosity, or because you want to improve your programming capabilities to stop people from exploiting your JS games. Given that the first thing I do when I open a new incremental is open the terminal and start messing around with your games, I figured it’s about time to write something about what I see and how I break your games. Consequently, I’ll describe ways you can protect your games from the basic code manipulations I perform. Some might say “you’re just ruining the game for yourself!” while I’m going to turn around and say “I don’t care” – that’s not the point of this!

NB: This will only apply to vanilla JS applications, which I see more commonly. Frameworks like AngularJS and such are out of scope for this post. Advanced techniques such as using a debugger, while slightly more on topic, will also be disregarded for now.

Lets talk about me for a second: I’m seiyria and I’m a professional mostly-JavaScript software developer. That is to say, I really like JavaScript – the ecosystem, the language, and the reach of JavaScript (did you know, there’s hardware powered by JavaScript? Awesome!). I’ve been programming for 10+ years now, sometimes I make applications, sometimes I make games – a little bit of everything.

Okay, enough about me, lets go into the things you’ll care about.

First, I’d like to talk about some basic client-side things that can be exploited. Later, I’ll get into protecting your server (as much as you can, anyway). It’s good to note that your game, if it’s client side only, can never be fully protected. You can take measures to do so, but there are more techniques not listed here that can be performed.

“Clean Code”

Clean code, or non-obfuscated code, is something that makes it very easy to logically see how your code works. This means that you’re basically uploading your code as you wrote it with no manipulation. It’s also very easily preventable. It won’t stop someone who’s determined to push through but 50% of the time I’ll look at it and go “eh, too much work for me to care.”

Essentially, what you should do here is just run your code through UglifyJS once before pushing it to a server. There are three benefits: A smaller JS footprint, less HTTP requests (assuming you concatenate them all into one file), and you’ve performed a small amount of mitigation, yay!

Public Functions

If all of your functions are public, ie, they look like this:

function myPublicFunction() {
  // perform awesomeness
}

or this:

var myPublicGame = {
  doSomethingAwesome: function() {
    // awesome level > 9000
  }
};

Then we have a problem. Lets suppose your “do something awesome” functions are game-critical functions like “level up” or “add resources” – it’s very trivial for me to open the console and go myPublicFunction() or myPublicGame.doSomethingAwesome(). What’s even better, is if your function accepts an argument for how much I get to increase my resources or level by. My favorite.

What can you do to fix this? Two simple ways; the first requires no real planning on your part, just put this around all of your code:

(function() {
  //your code here
})();

What does that do? Well, it puts all of your code in an isolated scope that can’t be accessed globally. This means that all of your code is inaccessible via the terminal, for the most part. The second way requires a bit more thought, but it lends itself better to an application design standpoint. JavaScript has a class-like syntax. I say class-like, because JS does not have traditional classes (not yet, anyway). Lets look at that syntax here:

var MyClass = function() {
  var myPrivateVariable = 4;
  
  var self = this;
  self.myPublicVariable = 10;
};

var myClassInst = new MyClass();

console.log(myClassInst.myPrivateVariable); // => undefined
console.log(myClassInst.myPublicVariable); // => 10

As you see here, you can fake private variables by limiting the scope they’re accessible from. Why does this matter? Well, lets make it a more practical example:

var MyGame = function() {
  var growthRate = 100;
  var myCurrency = 0;
  
  var grow = function() {
    myCurrency += growthRate;
  };
  
  setInterval(grow, 1000); // grow more every second of my life!
};

If you’ve been following along, you’ll notice that I can no longer go into the console and type MyGame.growthRate = 10000000000 or for(var i=0; i<10000; i++) { MyGame.grow(); } – it’s all private! There are a few more approaches here that could be taken, such as using RequireJS or other tools to manage your files, but lets keep it simple for now.

Some games that have exploits like this available:

LocalStorage modification

Maybe a little overkill, but if your game stores things in localStorage, it’s probably vulnerable. Most games I see just store a simple hash object with some data, or store a bunch of keys with data. Suppose you’ve protected your game via the above measures and now you want to make sure everything is good to go. Lets look at Meme Clickers data (here is an example). All I have to do is modify the memes attribute to be whatever I want, reload the page, and it’ll be peachy - the game won’t even know I messed with it; for all it knows, that’s a valid state. Similar case with Blackmarket – check this out.

“Alright,” you say, “what can I do about that?” Simple. Store a hash of all the data. MD5, SHA-1/2, anything. If you hash all of the data you’re saving, store the hash with it, and then load the game, all you have to do is verify the hash upon loading the game. If the hash is invalid, the save is invalid, and should be treated as a fresh start.

Server Exploits

Okay, so this is what prompted this article. Recently there was a game introduced called IncrementalGame. It’s pretty meta, and it’s also backed by a server. That last bit is what makes it a much more fun target than other games, since other people can see what I’m doing, too. Yesterday, I posted a simple exploit that allowed anyone to massively increase the votes behind any game listed there. I simply dug around in the code until I came across something that looked like it did something, watched my Network tab in my dev tools, and figured out how the game worked. Here are some things to note about having a game with a server:

Conclusion

In short, it’s very easy to make a game that’s exploitable. Hopefully the techniques listed above not only help you grow as developers, but make your game and have it played the way you intended.

If not, I’ll be there to break it.

Want me to take a look at your game / app? Send me a message!

comments powered by Disqus