Game Server security

Started by
10 comments, last by Dave Weinstein 11 years ago

Hello guys!

Right now I am developing a game consisting of a server made in C# .NET with database and a client for mobile devices in Unity3D.

I am using my own networking library with TCP.

The problem is: I am really afraid of people exploiting my networking code. So I know I can solve problems with the client by testing, testing, testing.

But what if someone writes their own client and doesnt keep my protocol? Right now I have a protocol which contains ( length of message | type of message | data ). What should I do if someone sends a request with a wrong length? Should I catch this case and just close the connection?

I think there are many cases my game server could crash. Is there any way of making sure someone can't crash my game server?

Maybe you guys can help me or point me to a paper which has this issue as a subject.

Thanks in advance!

Advertisement

There is only one rule in networking: never trust the client.

When you are going to receive a packet from the client:

  • Figure out the maximum size a packet should ever be, and read only that much.
  • Check the message header to ensure it is the right type of packet.
  • Check the message length to ensure it makes sense for the packet type.
  • Explicitly null-terminate any strings received from the client.
  • Check the range of any numbers received from the client.
  • Check the rate of change of consecutive values received from the client.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Check the rate of change of consecutive values received from the client.

Which values are you talking about?

Should I catch this case and just close the connection?

Yes. As soon as something is not right, close the connection. Also, log this to some counter, perhaps correlated with client version reported on connection, so that you can tell if you have a bug that causes users to get disconnected because the client sends bad data :-)

Also, put a stringent max size on EVERYTHING. A maximum size on the message type. A maximum size on files downloaded or uploaded. A maximum size on strings. A maximum item count in arrays. And make those limits be fairly limited. This will avoid someone doing things like sending a "valid" packet that claims to have 2,000,000,000 items in it, and thus tying up your sever just receiving and dealing with that packet.

Another popular attack is sending malformed inner data. Say you have a "login" packet that looks something like: name:string, password:string, clientversion:varint.
Now, if strings are zero terminated, then someone can send a valid outer packet (type: login, length: whatever) but then not include a zero byte in the payload. Your decoding function may at that point run past the end of the packet, and read random memory. Don't do that! (Better is to length-prefix strings, and automatically cut them off at 255 bytes max.)

Even if you fix the string, someone might send a badly encoded varint. For example, let's say that varints are encoded with the high bit set when there's a continuation byte, and the high bit clear for the last byte; each lower 7 bits are data, little-endian. For example, the value 0xAA55 would be sent as:
(0xAA55 & 0x7f) | 0x80, ((0xAA55 >> 7) & 0x7f) | 0x80, ((0xAA55 >> 14) & 0x7f)
Now, someone could send a packet that contains the value 0x80 over and over again, causing your decoding logic to never break out of the varint decoding loop. Always establish a maximum length for a varint, or any data type, on the wire.

Once you have the serialization/de-serialization dealt with, the semantics of the packets matter. Don't treat something as "known good." For example, if an entity ID is in the packet, look up that entity ID in a hash table each time you see it. If it's not there, disconnect the player. If a property ID is in the packet, make sure it's valid, and if it's not, disconnect the player. If a floating point value is in the packet, make sure it's not NaN or Inf or denormal -- if it is, disconnect the player.
enum Bool { True, False, FileNotFound };

Which values are you talking about?

Assuming UDP networking, any sequence ids you are using to identify packets. It's ok to have a packet that is, say, 1-50 packets out of sequence, but if someone hands you a packet with a sequence number that is hundreds out, either someone is mucking with the packets, or you are experiencing massive packet loss, and might as well toss the client anyway.

You can of course also extend this to numeric values that change over time within the payload of the packet. But in general, I'd say that the client shouldn't be authoritative for any continuous values...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

You can of course also extend this to numeric values that change over time within the payload of the packet. But in general, I'd say that the client shouldn't be authoritative for any continuous values...

I would argue that clients are authoritative over the mouse position/movement, and mouse movement translates into camera/aim orientation (assuming FPS-style controls) and thus the client is authoritative for aim. While this makes aimbots easier to write, it's a necessary trade-off (and even without it, aimbots are entirely possible.)
enum Bool { True, False, FileNotFound };

Just create a series of checks which may reveal that the client is not a legitimate client or hacked client (i.e. detecting if the client is flying on the map - and not allowing that). Once that happens just remove them from the client list.

Making the connection-establish code as advanced as possible to verify the client can almost always be reverse engineered, so I wouldn't worry about that to much.

The problem is: I am really afraid of people exploiting my networking code. So I know I can solve problems with the client by testing, testing, testing.


When you start talking about exploits and security, you need to change your mindset into one of risk management and risk mitigation.

The two big questions are: Who are you afraid of? What are the costs of a successful exploit?

A small market hobby game really doesn't need to be worried about major groups. Maybe a few programmer-types will poke and prod at it a little bit. Maybe some people will attach a debugger or a network probe. This is very different from a mainstream game that can expect major attacks from organized attackers for an extended period of time.

You need to figure out the maximum costs of an exploit. Would a successful attack take down one game, or an entire server? Would a successful attack have any high profile targets, such as a public leaderboard that they want to climb, or externally-visible achievements? Does it take down financial information, or even allow an attacker to make real cash transactions or expose financial information?



If you are a small project with a low likelyhood of skilled attackers, and the reasonable risk is that an attacker's game will crash and possibly a server needs to be restarted, that is one thing.

If you have a low likelyhood of skilled attackers, and your biggest risk is that your accounts database could be corrupted or damaged but otherwise individual games would be unchanged, that is another.

If you are a larger project with a high likelyhood of skilled attackers, with risks of highly public exploits including potential financial concerns, that is something altogether different.


Rolling back a leaderboard and tracking down the bad accounts is easy.
Rolling back a server's account database to a known good state is hard, but possible.
Having millions of customer financial records exported is impossible to roll back.

Spend your resources based on a careful risk assessment. If the stakes are low, it probably isn't worth much effort.

Thank you for all your replies.

So this game is a MMO. Its not exactly a mainstream game, but players will access one server. I dont trust the client so far as that he cannot really cheat in my game or so.

The thing I am really worried about is that someone sends a bad package and this raises an exception on the server(ArrayOutOfBounds, InvalidMemory etc.).

I don't need to worry about really proffesional attackers, but if someone attempts to hack the server and the server crashes, all players will be kicked and this will be really annoying for them.

So my game is a combination of turnbased combat and trading card games for mobile plattforms. How likely is this to be hacked if it reaches 1 million downloads(really optimistic thinking, but for security issues the worst case)?

I think I'll think my networking lib through and check on how it could be hacked and catch those cases. Then it'll hopefully be enough.

The thing I am really worried about is that someone sends a bad package and this raises an exception on the server(ArrayOutOfBounds, InvalidMemory etc.).

So check the data when you read it. Validating input is not a difficult problem.

  • Length of message is too long? Log the event, drop the connection.
  • Type of message isn't valid? Log the event, drop the connection.
  • Data you try to read doesn't match the type of message? Log the event, drop the connection.

This isn't really about security but about code quality. These are all issues that could also arise if your client is not 100% bug free. The answer is just to make the server robust. Make sure your serialisation code is as small as possible, fully tested (eg. with unit tests), and always operates with reasonable limits in mind.

Also, wrap the code with exception handlers and kick off bad clients at the first sign of problems. You know you can catch all possible exceptions with a single catch block, right? That can be your final line of defence when running the live server to isolate problems with one client from the rest of the server.

This topic is closed to new replies.

Advertisement