Best way to protect RemoteEvents against exploiters?

I “solved” this age old problem with a sort of elegant solution. While you can never truly secure RemoteEvents, the solution I came up with will deter most of the exploiters who just watch YouTube videos on how to haxx adopt mee and have no real programming experience, or any idea of what they’re doing in general. I’ll outline my systems below:

Event name scrambling

This one is pretty common. This involves storing a cache of the RemoteEvent objects alongside the original names somewhere secure, like the server. Then ever second or so you rename each event to a GUID. This along deters most exploiters because while they can still see the traffic of the RemoteEvents, the names will always be changing, making it incredibly hard to track down specific events.

In my application the server holds the master list between the original event name, and it’s physical object in ReplicatedStorage. The server is also the one scrambling the names. This ensures the clients can’t stop the scrambling. When the client joins the game, it invokes one remote function that isn’t scrambled. This function returns the list of RemoteEvents and their objects. The server then marks that they’ve called it and won’t allow them to call that again.

Note: I use a custom NetworkingService that ties all these things together.

Rotating key argument

This one is fantastic. This involves the internal NetworkingService attaching a GUID alongside the event that’s fired. This is different than a password because those aren’t secure and are genuinely pointless. Every time an event is fired, the server returns a new GUID. It expects this to be send back when that event is fired again. If they don’t match, the server throws out the request. These GUID’s are unique to each RemoteEvent and rotate each time they invoke.

This is important because while the GUID’s are visible to the exploiters*, they are meaningless without an explanation. If a key is sent more than once, the events won’t go through.

Note: Upon testing this solution with Synapse, I found that whatever version of RemoteSpy I was using failed to present the data returned in a RemoteFunction. I was able to see the initial request and it’s data, but anything returned by the server was not visible. If this is the case with all current versions of RemoteSpy, it’s essentially impossible to know what they next GUID is.

Things to know

You cannot store anything in a module. The list of RemoteEvents to Objects, the master GUID table on the client etc. All of these things must be stored in private variables so they’re not accessible by any malicious actors.

Final remarks

Like I said at the beginning, you can never truly secure RemoteEvents / RemoteFunctions. These solutions just deter a lot of malicious actors from perusing more exploits in your game. I only recommend these solutions if you’re experienced with writing internal libraries such as NetworkingServies and whatnot. These systems can fail incredibly easily if not setup properly. I’ve fine tuned my module to work reliably, without problems. If you have any questions please let me know.

16 Likes

That would then go back to my second argument of encrypting the key

1 Like

All client side anti-exploit measures are bad at stopping exploiters. I’m 99% sure that they have or are figuring out your key system right now. The best anti-exploit measure would be not relying on the client in the first place.

2 Likes

For some reason this got bumped even though the last reply was more than 2 weeks ago.

If you want to stop exploiters from spamming remote events, I found a solution.

--Localscript

local SendDelay = 0.1

while true do
    Remote:FireServer(..., workspace:GetServerTimeNow())
    task.wait(SendDelay)
end

--Server script

local SendDelay = 0.1

local ServerStartTick = workspace:GetServerTimeNow()

local PlayerLastTick = 0

Remote.OnServerEvent:Connect(function(Player, Tick)
    --Is the client not waiting enough?
    if Tick - PlayerLastTick < SendDelay then
        Player:Kick("Remote spam!")
    end
    
    --Is the client trying to break the system by sending a value ahead of the server?
    if Tick > workspace:GetServerTimeNow() then
        Player:Kick("Back from the future?")
    end
    
    --Is the client acting like it started running BEFORE the server?
    if Tick < ServerStartTick then
        Player:Kick("Back from the past?")
    end
    
    --Less accurate, they could just have had a ping spike, but better to try and cover everything
    if workspace:GetServerTimeNow() - Tick > 5 then
        Player:Kick("Your internet connection is TOO unstable!")
    end
end)
3 Likes

I’ll give you secure example

Add a boolvalue in character model
everytime you pick up a coin make it true through what ever method you use to pick up the coin
(Preferably not .touched)

then fire the remoteevent making sure to check if boolvalue is true when the remoteevent is onServerEvent before proceeding to add the coin

Let’s say you’re making a script inside a part that checks whether you clicked on it or not with your mouse (Local > Server).

You also want to add a distance the click should be, so let’s say 10 studs.

The way this RemoteEvent can be manipulated is that it can bypass the distance and can just click anything interactable on the server.

The solution is to actually check if the player is 10 studs or closer to that part, in order to click, in the server.

In hindsight, just add extra security in the server, and a more useful addition to this can be to kick the player using the variable player from the RemoteEvent, if the 10 stud radius is out of reach from the player.

What if your legit local scripts use a remote event every RunService RenderStepped? That puts limitations.

It… shouldn’t? If someone uses an FPS unlocker and has a really good PC, people could really easily DDoS the server without even realizing.