In theory, a lot of damage can be done with RemoteFunctions.
For example, it is typical for a combat game to have a “damage(Player, amount)” RemoteFunction, which could be used to kill everyone instantly.
But does anyone ever actually succeed in exploiting a RemoteFunction more sophisticated than “givePoints:FireServer(amount)”?
Because I’ve never heard of anyone exploiting a RemoteFunction with anything more than a single NumberValue argument.
No. You should never have a remote that just does whatever the client tells it to do. Anyone can call any remote with any arguments. Instead of damage(player, amount), you should have something higher level that represents the action you want to support, like shoot(direction), then have the server process the shot and deal damage without relying on the client for any other information.
How safe are RemoteFunctions?
As safe as the way in which you make use of them. If you have a god remote in your game, then you’re not being safe. Just make sure to put server-side checks on all of your remotes so that players can’t do something they shouldn’t be able to do at the time when they call the remote.
I use a bunch of remotes(events/functions) in my game and we kick people when the input parameters are malformed in the slightest way. You don’t want to know how many people get kicked and this is a low traffic game we’re talking about. It is very common to have people messing with remotes in your game, so server-side checks are critical.
If used incorrectly, they are not safe at all. However, they could be very secure.
- You must assume that the exploiter knows the names and locations for all your remote events and functions, as well as the parameters. They can also fire them. Because, well, they can.
- The server should always be in charge of the client, and the client should almost never have the ability to control the server. (Exceptions include animations, Network Ownership, etc.)
Remotes should serve as a way for the server and client to communicate. Instead of telling the server what do change, the client should request an action, and your server should verify before executing anything. You mentioned the examples of giving points and dealing damage.
- Dealing Damage: Damage is usually checked from the server. For example, if you have a game that allows you to cast ‘magic’, then you would use a remote to cast the attack. The server would make sure you have the ability to make that attack, and it would check for cooldowns and any other factors planned. If all the checks work, then the server would make the attack and check for any hits. There’s obviously a variety of ways to go about dealing damage, but having a damage(Player, amount) is a major security risk. You would somehow need to verify that the client is not lying, on the server.
- Giving points: If the client needs the player to gain points, it should inform the server why, and the server should respond accordingly. Lets imagine we have a gui with a ‘claim daily bonus’ button. When it’s clicked, we would want the server to make sure that the client has not already claimed the bonus. The server would then give them the correct amount, without the client telling them the amount.
So instead of having your remotes work like this:
givePoints:FireServer(amount)
You would want them to look a bit more like this:
givePoints:FireServer(reason)
TL;DR: Your remotes should be like a request from the client, and the server must make sure the action is correct before executing anything. As others are saying, server sided checks.
Serverside checks are all that (always) work. I’ve seen people having systems where they encrypt data, send along a (dynamic) key to “prove” it got called by the right script, … which’ll stop a lot of exploiters (in the beginning), but there’ll always be someone who can around any encryption or other (semi-)clientside safety feature. Serverside checks are the only things that’ll always work.
TL;DR: Serverside checks. Serverside checks. Don’t overcomplicate the server<>client interaction, it’s mostly annoying just for you. Serverside checks.
RemoteFunctions are very safe, as long as you use them properly. As long as you do sanity checks on the data sent from the client (e.g. does the client own the item that they asked to equip?), your game will be secure.
It’s as safe as opening up a web endpoint on a website. In other words, assume anyone can hit the endpoint. That means you have to “code” the endpoint to handle malicious requests.
A good rule of thumb in game development is to never let the player/client make a change on your server. So don’t use a remotefunction to allow client code to make an important change, such as setting a player’s XP or whatever.
As everyone here has said RemoteFunctions are as safe as you can make them, and never trust the client.
I wanted to add a note where you don’t always need to use RemoteFunctions!
RemoteFunctions are used when you want to tell the server something AND expect it to tell you something back in turn. HOWEVER RemoteEvents are used to send information without wanting anything back in turn.
In your case you want to tell the server to damage the player a certain amount, now you may need the server to tell you something when it does that and you may not, in the case of the latter I’d recommend using RemoteEvents.
As the wiki shows when using RemoteFunctions you assign it to a variable
local didDamage = damage:InvokeServer(Player, amount)
if didDamage then
print("Player has been damaged!")
else
print("Player was not damaged!")
end
In this case your code waits for the server response, and acts accordingly
damage:FireServer(Player, amount)
print("Requested to damage player!")
In this case your code does not wait for the server response and goes on!
Better tutorial can be found here!
I do security testing as a hobby, and from my personal input, no amount of security can stop one from figuring out and replicating what your remote does if they have enough resolve to do it.
I suggest sticking to serverside checks and no giving client power.
Think you are the owner of a bank. You wouldn’t go around giving your clients a key to the vault telling them to “grab ONLY the amount you have”, because you’d know that not everyone is honest. Instead, you’d tell them to go to the cashiers, ask them for an amount and the cashiers would check if you have that money - if you have it, they would simply give it to you.
That’s the concept of remotes: don’t make them the “keys” to your “vaults”. Never trust the client.
Always do server-side checks on anything the client asks, by making it the “cashier” to your bank: have them see if what the client is asking is valid, and if it is, proceed normally.
And always ask this question for every remote you create:
- If an exploiter could call this remote however they liked (which they always can), would they abuse it (by giving them extra money, by killing everyone, etc)?
If your answer is yes, delete the remote and start again.
RemoteFunctions in particular are unsafe when called by the server. That is, when InvokeClient is called, the calling thread is yielded until the client has responded. A malicious client can choose to not respond at all, blocking the thread forever, and potentially eating up server memory.
The general issue with the RemoteFunction is that, when it blocks a thread, the single condition required to resume it relies on the compliance of an external peer. This can be seen as a bad practice, since it effectively gives the external peer limited control of the thread.
Until a secondary condition (such as a timeout) is available, I would recommend avoiding RemoteFunctions entirely, and sticking to RemoteEvents for peer communication (although the RemoteEvent has its own problems).
What happens when a client is not answering an invoke call (yielding the server thread) and it leaves or is kicked? is the thread ended?
Edit: I got my answer from the wiki:
Warning: If a client disconnects or leaves the game while it is being invoked from the server, the InvokeClient function will error. It is therefore recommended to wrap this function in a pcall so it does stop the execution of other code.
There might be a typo though, I think it should read “does not” in the last sentence?
article
Typically, all your damage things should be handled on the server side, and not the client-side.
If you have a key that doesn’t change at all during the session, it has little to no effect on preventing exploits.
AFAIK most of the current exploits work by tapping into the remote calls, and displaying what data the client sends to the server. If you include a static key as mentioned in your example, the exploit might show:
workspace.Damage:FireServer called with data: "18152121524271", game.Players.Roblox, 100
The exploiter now knows your key, and can just include it in when calling FireServer on their own.
Having the “secret” key change over time or depend on the submitted data might help a little, but someone could still view the client side source and figure out how to bypass it.
Most exploit(er)s do have access to “remote spy” or anything that basically replaces FireServer/InvokeServer for logging purposes, although it’s not too difficult to have it modify the values being sent/received, or just completely filter requests:
function eh:newFireServer(...)
if self.Name == "TakeFallDamage" then return end -- filter
if self.Name == "GiveDamage" then -- alter damage
local target = ...
return oldFireServer(self,target,1000)
end return oldFireServer(self,...)
end
function eh:newInvokeServer(...)
if self.Name == "IsAdmin" then -- alter returned values
local isAdmin,requiredValidartionCodeThatsAlwaysSent = oldInvokeServer(self,...)
return true,requiredValidartionCodeThatsAlwaysSent
end
return oldInvokeServer(eh,...)
end
obviously those RemoteFunctions/Events are (without proper serverside checks) just insecure in general
Tl;dr assume any client side security can be bypassed.