How to use RemoteEvents properly

a guide written by Kampfkarren

This assumes that you use FilteringEnabled. If you don't, you should.

It's a common misconception that FilteringEnabled stops hackers. It doesn't. That's not its job. FilteringEnabled stops hackers from having their hacks replicate to other clients. FilteringEnabled does not stop hackers from injecting Lua code. This is why it is important that your RemoteEvents are secure.

There are three golden rules about networking.

  1. Never trust the client.
  2. Never give the client too much power.
  3. Everything that can securely be handled on the client should be handled on the client.

Never Trust the Client

By "never trust the client", I mean never trust the client.

Let's say I had a Server Script that lets players deposit money into their bank accounts.

game:GetService("ReplicatedStorage").DepositMoney.OnServerEvent:Connect(function(ply, num)
    ply.Moneyz.Value = ply.Moneyz.Value - num
    ply.Bank.Value = ply.Bank.Value + num
end)

Do you see the problem? We don't check if the number is at all valid. Do we check if it's a number? No. Do we check if it's negative? No. Do we check if they have enough in their account? No.

A hacker can run this line of code:

game:GetService("ReplicatedStorage").DepositMoney:FireServer(-999999)

And they'd be RICH!


What's the solution? Easy.

game:GetService("ReplicatedStorage").DepositMoney.OnServerEvent:Connect(function(ply, num)
    if typeof(num) ~= "number" then return end --Is num a number? If not, stop the script. I kick the player here.
    if num < 0 then return end --Is num negative? If not, stop. Again, I'd kick the player here.

    if ply.Moneyz.Value >= num then --Do they have enough moneyz?
        ply.Moneyz.Value = ply.Moneyz.Value - num
        ply.Bank.Value = ply.Bank.Value + num
    end
end)

Get the point? Never trust the client. Make absolutely sure their inputs are valid before doing what you want.

Never Give the Client Too Much Power

Let's say we had a gun, but because we want to avoid latency, we handle all the firing on the client.

--DO NOT USE THIS

tool.Activated:Connect(function()
    for _,victim in pairs(getEnemiesInFiringRange()) do
        game:GetService("ReplicatedStorage").DamagePlayer:FireServer(victim)
    end
end)

I'm sure (and if you can't, I'm scared) you can understand the problem here. A hacker can just run this.

while wait(1) do
    for _,player in pairs(game:GetService("Players"):GetPlayers()) do
        game:GetService("ReplicatedStorage").DamagePlayer:FireServer(player)
    end
end

Oh my. What's the solution? Sadly, we must accept the latency. Players might complain about lag in your game, but tell them it's better than having hackers being able to kill everyone.

I hope this helps you with securing your game. Remember the three golden rules when developing RemoteEvents, and hopefully you'll be fine.