Before I dive into the problem myself figure it would just be easier to ask here first. What do you guys do to make sure that webrequests sent to a php file are only accepted if sent from within the app?
I’m going to save you many hours or days of angst:
It’s not really possible. There are schemes that attempt to do this, but they’re all easily defeated.
This may be a bit of an X/Y problem. What is your motivation? To prevent cheating?
What a lot of software does it to sign the message with an encrypted signature, that can only be understood by your server. When your php server receives the message, it decrypts the signature and makes sure that the message content matches the signature.
However this requires the build you distribute to include the signing key. Eventually, if people are motivated enough they can decompile your binary and pull out or reconstruct the signing key. I won’t say this is trivial, but it is often doable.
With that key malicious users will be able to craft their own webrequests that look legitimate to your server.
The way large software and game companies combat this is to partner up with third party anti-cheat providers like Battleye, Easy Anti-Cheat, VAC, XIGNCODE3, ect. These products either embed themselves into your build, or run alongside it and do some pretty complex stuff like monitoring running processes on the target machine, hooking memory allocations, or watch for known cheat software.
Unless you’re a large company with money to burn on anti-cheat software, you will probably be far better off ensuring that whatever game logic your server is doing, has a zero-trust policy with any data it receives from clients.
For example, If your server is instructed by a client that they want to move to XYZ position, validate that they should legally be able to make such a move. Likewise with a first person shooter, the server should be running the logic to calculate hits, not trusting any client that they made a successful hit.
I hope this has been insightful, it is obviously a huge and complex topic. I would be interested to see how you end up doing it.
Edit: I should mention, there’s bound to be a bunch of cool ways people have come up with to provide some security to their networking. What I offer above is more of an outside perspective on what I’ve observed to be an industry trend rather than a Unity specific implementation of these concepts.
I’ll try to make a little decision helper for you:
- Default
Motivation: deter the curious observer (unmotivated attacker, your ISP, agencies, whatever).
Solution: Use https. You are doing this already right, RIGHT?
- Deter the unmotivated / unskilled
Motivation: deter the curious hacker who is not motivated enough to actually make an effort other than trying to replay/alter requests to your server (highscore cheaters and spammers are the most common type I have seen falling into this category)
Solution A: Hash your data (content + salt) and check that hash on the server.
Advantage: You can observe it easily while others should have trouble altering it. Your API is still an open book. Design it accordingly.
Solution B: Client side encryption
Caveat: While others will have more trouble observing your data, you will have that trouble too while debugging. Also, you may be subject to crypto exporting regulations (say hi to the BIS for me). Is it worth the trouble?
- Deter the motivated / skilled
Motivation: Your economical survival depends on it.
Solution: All logic is on the server. The client is just a fancy renderer which forwards input to the server. And even that can be hacked.
I once heard this rule of thumb (I paraphrase):
If you have 50.000 players then you will have done something to really aggrevate 500 of them. Of those 500 at least 5 will have the skill to hack you. Of those 5 one will actually bother doing it. It’s not a question of IF, it’s a question of when.
Even made a meme* for you.
* Yes, I love that movie!!!
Hope this helps ![]()
Yeah I basically already have a function on the server that generates a hash whenever they do something and everything is validated on the server.
The easiest way is when they login to return a key and add it to a key already in the app to generate a hash for sending server requests which should pretty much deter 99.99% of people. I might even look into the headers of the webrequest and see if something there can also be used as an extra step.
With all the validation done on the server it is extremely secure however I am just looking into adding a few simple extra measures to deter most people from even trying.
Right, you can implement all sorts of validations, the more unrelated metrics the better. However you have to be careful to not detect false positives. So depending on what platforms you want to support, you would need one or more test device(s) for each platform to make sure you get the same reliable result.
Though one of the best counter measures is to play dumb. So you could include a hash of the data, challenge / response measures, extra http headers and what not. The important part is how you react on the server when you detect an attempted manipulation. The best response is: pretent everything worked just fine
Having unexpected reactions can easily throw off cheaters. Common tactics are:
- acknowledge clearly manipulated requests, but don’t actually carry out the request.
- In addition to the first point, you can also remember the sender temporarily in a seperate database where you can temporarily simulate the success, just to revert all those actions after 10 minutes or 1 hour. So any communication with your backend would work on a faked database so the cheater thinks he has fooled your server but it just backfires later.
So for example if you just want to implement a highscore list, when you receive a clearly faked request, you may temporarily add the new highscore to the list but flag it as cheated internally with a timestamp so it gets removed automatically after a certain time. So the cheater thinks he was successful but the highscore list cleans itself from manipulated requests.
