Contents
- Introduction
- Why Care?
- How Do Cheaters Cheat?
- General Tips
- Back to the Problem
- Cryptography: Your New Best Friend
- Putting it to Use
- What's Next
- Contest
1. Introduction
Cheaters: the human race is full of them. I'd be willing to bet that every single person reading this article has cheated at least once; most likely more than once. People cheat in almost all areas of life - school, taxes, business, relationships, games and competition. The list is endless. It's almost as if the human psyche were created with a powerful, insatiable drive to cheat. Anything to get ahead, right? For those of us who wish to play fair, this can be a real problem. Of course, I'm talking about cheating as it relates to online games, and more specifically, BYOND games.
I first began contemplating this problem back in November 2001, when I thought it might be fun to hold a contest offering a small monetary prize to the first player to reach a score over 100,000 in <a target=_blank href=http://www.byond.com/hub/AirMapster/ShapeShifter>ShapeShifter (it's since been done, and no contest was ever held - sorry Z!). I quickly realized that if the prize were large enough, some people may attempt to "win" without ever playing the game. If Chester Cheater (no relation to snack food eating felines) thought he could figure out how scores are sent to the server and fake a high score, he might be determined enough to try it. Then I became obsessed with the question: How could I verify that any score received at the server was legitimate and not faked? At the time, the answer was that I couldn't. If Chester were good enough, he could fake it completely without my knowledge.
This series of BYONDscape articles will document my progress in attacking the problem. It will focus on my high score library and the security challenges associated with ensuring that high scores won't be tampered with. As I learn, so will you and the rest of the BYOND developer community. While I will focus primarily on my own problem, the ideas presented herein will hopefully be general enough to apply to many, if not a majority of BYOND games.
In this article, I take a broader look at cheating, examining the methods by which players like Chester can cheat, and introduce the techniques that can help us combat the problem. The articles yet to come will focus more in depth on the specific problem of high scores, scrutinizing the system for potential vulnerabilities, and finally offering a detailed introduction to encryption as an effective weapon against the cheater.
2. Why Care?
Why do you, as a developer, care about who might cheat in your game?For starters, nobody will play if it has a reputation for attracting cheaters. If you would like to make money from your game, expect a significant dip in sales when word gets out that it is a cheater's haven. What fun is a game where the people on the other end can't be trusted to play fair, and may have built up their characters through illicit tactics?
If you're like me, and plan to hold a contest with real prizes for the best players, you'd better be absolutely sure those players aren't cheating. And as the value of the prize goes up, so does the incentive to cheat.
3. How Do Cheaters Cheat?
While the question of why people cheat may be interesting, it's not of much importance in my opinion. We already know that they do, and will often go to great lengths (short of playing fairly) to accomplish their goals. The real question that needs an answer is "How?" Armed with this information, we, the developers, can design our games to thwart cheaters by making it next to impossible to cheat.3.1 Exploiting bugs
No matter how good you are, there will be bugs in your code. Some of these bugs may work to the advantage of the cheaters - for example, if repeatedly and quickly entering a command allows Chester to advance in strength beyond what was meant by design. If your game is popular enough, Chester and friends will find and exploit these bugs.
3.2 Information Attacks
Information attacks are a class of cheats that use information sent to, or received from the game in order to gain an unfair advantage. These types of attacks are really at the heart of what this article is about. If Chester can send bogus information to the game or receive information that wasn't meant for him to view, he can cheat. In both cases, compromised information can be either realtime game status data or stored data.
3.2.1 Information Sent: Who Do You Trust?
Trust is a big issue for most online games. Depending on how the communication is setup, each participant may be given trust to provide accurate information about some part of the game. It may be real time information, such as the player's position on the map; or it may be stored information, such as the player's strength or wealth. If a player like Chester betrays that trust, then the entire game could be nullified.
3.2.1.1 Networking: How Realtime Game Information is Gathered and Communicated
Many online games today use a peer-to-peer model for network communication. Each computer runs a complete copy of the game, and they all contact each other with authoritative updates about the state of the game. Consider a gaming session in which Adam, Byatrice, and Chester all connect to play a game together. When Adam directs his character to run down a hallway, his computer tells the other two Adam's new position and they update their copies of the game accordingly. Chester is more devious: he has modified his copy of the game to allow him to run through walls. When he does so, his computer tells the other computers that Chester is now on the other side of that wall, and they blindly accept it as fact. Chester jumps through a wall and kills a very unsuspecting Byatrice, who had every reason to believe she was hiding in a safe place. In simple peer-to-peer networking, absolute trust is placed in every participant.
BYOND is built on a client-server network architecture. One computer is the server, which knows about everything that is happening in the game. The clients are dumb: they only display what the server tells them to display, and send player input back to the server. Furthermore, the server does not trust the clients to make any game-determining decisions; all they may do is provide player input. If Chester wants to run through a wall that he shouldn't, he's out of luck: his computer can only tell the server that he wants to go in that direction. The server will determine that there is a wall in the way, and therefore the move is invalid. As long as the server is trusted and secure, the game is secure because no client can do anything the server won't allow.
BYOND can do peer-to-peer networking; it's just not as easy and transparent as the client-server architecture upon which the entire system is built. Currently the only way to do this is to have separate servers contact each other using <a target=_blank href=http://www.byond.com/docs/ref/info.html#/world/proc/Export>world.Export(), <a target=_blank href=http://www.byond.com/docs/ref/info.html#/world/proc/Topic>world.Topic(), and <a target=_blank href=http://www.byond.com/docs/ref/info.html#/world/proc/Import>world.Import(). In this case, each computer in the top figure is actually an independent server - it may be acting as its own client, or it may have several other clients connected. Because these procs do not allow for persistent connections between worlds, we will likely only see peer-to-peer situations when different worlds need to communicate briefly with one another.
ShapeShifter uses a mix of client-server and peer-to-peer networking. The game is distributed to run locally on each player's computer as its own server, using the standard BYOND client-server model. When a game is over, it contacts a central high score server and reports that player's score. This is the peer-to-peer connection; and because the central high score server was not involved in the actual game itself, it must place absolute trust in the player's computer that it is reporting a valid score. Therein lies the problem - this transaction opens up the possibility to cheat for ol' Chester. I'll get into this more later on.
3.2.1.2 Data Storage: How and Where Important Game Information is Stored
The other potential area for a trusted party to cheat is by modifying important game data. Consider an RPG that stores character data such as strength and wealth on the player's computer. Each time the player connects to the game, character data is sent to the server and the player's character is loaded based on that data. This model of data storage can be nice for a few reasons: the server doesn't get bogged down saving data for hundreds or thousands of characters; and players may be able to transfer their characters among several different servers. The problem is that absolute trust is placed in the player to provide an accurate character data file. Chester won't have any of that! He'll do everything he can to edit the data stored on his computer and make himself more powerful than other players.
In BYOND games, this is most often done with client-side savefiles. It would be trivially easy for Chester to write a short BYOND program to examine and edit his character savefile, without the server ever knowing. But don't give up entirely on client-side savefiles! Thankfully, there are measures that can be taken to thwart this activity, as will be seen later on.
3.2.2 Information Received: Letting the Cat Out of the Bag
The final major way players cheat is by finding out information they aren't supposed to know - this is often called "information exposure." If Chester has superior information, he can make better decisions and gain an unfair advantage against his opponents. This is essentially the opposite of the trust issue: in that case, players were sometimes given the opportunity to send false data to the game; in this case, players are given the opportunity to receive extra data from the game. As with trust, there are two ways this can be exploited: Chester can view extra information that is sent over the network but not intended for him to see, or he can view extra data that is stored on his computer.
In commercially available online games, this form of cheating generally involves hacking the game executable, video drivers, or running an auxiliary program to gain information from computer memory that is not accessible through the game interface itself. For example, a popular cheat for many 3-D games was to hack the video driver so that all walls would appear transparent on the cheater's screen. Using this cheat, Chester could see when Adam was behind a wall between them and easily setup for the kill. In this case, the data identifying Adam's position is sent over the network; it is simply not meant to be seen by Chester unless there are no obstacles between them.
The BYOND server sends information about all atoms in the client's view, whether or not the client should be able to see them. The DreamSeeker program has the job of filtering out what is visible and what is not, and then displaying that to the player. This opens up a small avenue for Chester to cheat - if he can obtain information about atoms that are within his view but he is not supposed to see, he will gain an unfair advantage. This is primarily a BYOND issue; therefore we won't concern ourselves with it in this article.
Client-side savefiles are also vulnerable to information exposure, just as they are vulnerable to a betrayal of trust. If any of the data you store in the client-side savefile could help Chester improve his strategy, be assured that he will attempt to find it.
4. General Tips
Now I'll look at some general techniques for foiling the insidious plans of Chester and company, focusing on cheats of the bug exploit variety. This is by no means comprehensive, but it should get you thinking about what you can do to spoil their nasty little fun.
- Think like a cheater. As you design and test your game, always think in the back of your mind about how you might exploit a particular feature to gain an unfair advantage over other players. Maybe there are certain situations when a particular statpanel gives more information than it should. If a player could hold down an arrow key and quickly move past a difficult part without getting hurt, maybe you should limit movement to slow it down.
- Perhaps more importantly, stay in touch with your player community. If your game develops a large following, chances are there are a few potential cheaters among them. Word of any cheats that are discovered will likely spread quickly, and the sooner you hear about them, the sooner you can fix them, keeping your legitimate users happy.
- Along with this, good users will want to know whether the player-hosted servers they connect to are running the latest game version with the latest fixes for cheats. Give players a way to determine what version they have connected to - it can be a message at login, an informational verb, whatever you like. That way, players who know a particularly bad cheat was fixed in version 35 won't waste their time with Chester, who's still hosting version 34. Also provide information for what cheats are fixed in new versions, whether on your game's hub listing or an information website you put up somewhere.
If your game allows hosts to change parameters that affect gameplay - the amount of damage done in fighting, for example - also give players a way to find this out. Nobody wants to connect to a server with wacky rulesets that are only fun for the host who knows about them.
5. Back to the Problem
Whew! Finally, after all that, I'm now ready to get into the real focus of this article. My problem with ShapeShifter high scores is that I must place absolute trust in each computer that contacts my high score server with a new player score. I have no way of truly knowing whether Chester actually played the game and got that score, or whether he wrote his own little program to mimic the communication and send any score he wants. As things stand today, this isn't so bad: Chester would have his work cut out for him. He would need to figure out:
- The BYOND address of my high score server
- Any parameters I send to the high score server in world.Export() from the game
- The format of the savefile I send to the high score server (oops! that's a valuable nugget of information right there: he didn't necessarily know I was sending a savefile)
- The password I store in the savefile for verification
All of this is doable by a determined cheater who really knows his stuff, but would take monstrous effort. However, I'm about to simplify Chester's work immensely. I plan to release my high score library so that anyone can use it - and therefore anyone can see exactly how I send scores to the server. Additionally, I plan to add the feature that if a player is offline when the game finishes, ShapeShifter will save the score in a client-side savefile and attempt to send it next time the player is connected to the internet (currently that score would be lost forever if your connection is down). I can see Chester salivating at this opportunity now: he'll just edit that savefile and add about 500 billion points to his score!
How can I release this information without compromising my own high score server? And how can I safely save a score on the player's computer and ensure it won't be edited?
6. Cryptography: Your New Best Friend
In order to thwart Chester Cheater and ensure the integrity of my high score database, I must accomplish two goals:
- Verify that any score sent to my central server is valid
- Secure saved scores on a player's computer from tampering
Cryptography gives us the tools to accomplish these goals. What is cryptography, you ask? The dictionary says it is "the process or skill of communicating in or deciphering secret writings or ciphers." The key here is "secret writings." We want to make the communications and data storage involved in high scores secret from anyone else, and especially Chester.
In a nutshell, cryptography deals with plaintext and ciphertext. Plaintext is the actual text or data you want to communicate or store. Ciphertext is that same information, garbled in such a way that only you or other people you choose (and trust!) can translate (decipher, or decrypt) it back into plaintext. Encryption takes plaintext and turns it into ciphertext. Decryption does just the opposite.
Here's an example. Adam wants to send a secret message to Byatrice, but doesn't want Chester to listen in. He and Byatrice agree on a method of encryption ahead of time, and they make sure Chester doesn't know about it. Adam encrypts his message and sends it along to Byatrice. Chester, sly fellow that he is, intercepts the ciphertext. All he sees is garbage - he can't make heads or tails of it. Byatrice, meanwhile, receives the ciphertext and decrypts it. She gets the message just fine.
Adam's message | Chester is cheating! |
Adam encrypts the message | C7B4BFDD24F7934C0F962721EBB40C8B53EE6881 |
Chester intercepts, sees gibberish | C7B4BFDD24F7934C0F962721EBB40C8B53EE6881 |
Byatrice receives ciphertext | C7B4BFDD24F7934C0F962721EBB40C8B53EE6881 |
Byatrice decrypts ciphertext | Chester is cheating! |
Not only is Chester prevented from reading the message (information exposure), he also can't change the message. That means he can't modify the message in transit to say something like "Chester is great!" He can try, though. Chester intercepts the ciphertext, replaces it with something that looks similar but changes around some text at the end:
Adam's message | Chester is cheating! |
Adam encrypts the message | C7B4BFDD24F7934C0F962721EBB40C8B53EE6881 |
Chester intercepts, modifies | C7B4BFDD24F7934C0F96272A4BD67E89AF9E8CB0282 |
Byatrice receives modified ciphertext | C7B4BFDD24F7934C0F96272A4BD67E89AF9E8CB0282 |
Byatrice decrypts modified ciphertext | Chester ò{€)>Á©” |
The result is clearly garbage, so Byatrice knows not to trust it.
7. Putting it to Use
We've finally seen that encryption can help safeguard our data from meddling kids like Chester. What now? Well, I suppose we need an encryption proc to use. This is really the subject of a future article, so for now I'll just toss out something for you to play around with. BYONDscape subscribers can download <a target=_blank href=http://www.byond.com/hub/AirMapster.Crypto.rc5Preview>AirMapster.Crypto.rc5Preview and try it out. This library implements the RC5 encryption algorithm, which is a high-grade cipher developed by PhD mathematicians. My implementation is complete, but a little rough around the edges. I have verified that it works and I currently use it in the released version of ShapeShifter.
I'll save the details for that mystical future article, but here is a quick example of how you can use it now. Suppose you want to store a client-side savefile and encrypt it so that player can't read it. Well, now you can! Observe:
#include client proc/ExportSafeFile(savefile/s, key) var/safetext = RC5_Encrypt(s.ExportText("/"), key) del(s) s = new s["/safetext"] << safetext src.Export(s) proc/ImportSafeFile(key) var/f = src.Import() if (f) var/savefile/s = new(f) var/safetext s["/safetext"] >> safetext if (safetext) del(s) s = new() s.ImportText("/", RC5_Decrypt(safetext, key)) return s |
If you compile the library by itself, the demo world will include this code, plus some verbs for testing it out. Try changing the key and seeing how it affects the ciphertext (and how bogus keys decrypt to bogus plaintext). Have fun!
8. What's Next?
I think that's quite enough for one article! In the next installment, I'll introduce the famed high score library for general consumption, walking you through some examples of how to use it, with and without encryption. The remaining article(s) will plunge headfirst into hardcore cryptography. Only the strong will survive! Just kidding... First I'll attempt to provide some more background on cryptography so that you can use it most intelligently in your own games. I hope to have a few more cryptographic algorithms implemented for your use by then, and I'll wrap things up by addressing the remaining issues with the high score scenario.
9. Contest
Just to drive home the point that the RC5 encryption algorithm is industrial strength, not your little brother's hack, I present a little contest. 256 BYONDimes from me to the first person who correctly identifies the plaintext that created the following ciphertext:
89F5963F040A4C1362E81669E6C6D7BAEC41213E 739E7900D1710B2B67F511A596BDDF21A629801A 1ED244C42338FF9B6DF4C790A174902B9D23AA1C 2D4FE482C78EAE3E60A590EBC65AE662 |
The line breaks are added simply for readability - it is one complete, unbroken string. The encryption library linked above was used to generate it and can be used to decrypt it with the proper key. If no correct answer is received within 3 months of the publication date on this article, the contest will be over and no prize awarded. Login to <a href=http://www.byond.com/script/dms.cgi?url=byond://AirMapster.rc5Contest>byond://AirMapster.rc5Contest and submit your answer. Enter as many times as you like.
I have a feeling I'll be holding on to my dimes, but you'll have to wait for a future article to find out why...