So recently I’ve made a new game and it works great and all, but the issue is exploit security, as is with most games. The reason this is so crucial in the scenario of my game is that a lot of the gameplay is based on sending data over events. I’ve got checks on all sides and what not, but I can’t prevent an exploiter from destroying my game in seconds through making an automatic playing script. My game is that of a clicker in which you buy items to get more money per click (sort of like Cookie Clicker), thus meaning that a lot of cross-communication is needed. I already have checks in place, I just need to be able to physically stop them from firing my events / invoking my functions.
Does anyone have any suggestions for security? If so, please leave them here.
For example, keep track of the last time someone fired a remote, and if it is fired again for that player within X time then reject the request. Another example is if they need to click on a physical object, but their character is miles away from it, you could also ignore the click.
Not possible. They are in full control of their client and can invoke whatever remote they want with whatever parameters at any time.
You could check the intervals between each remote that’ll tell if they have something auto going on so if it doesnt look like a human could make those intervals kick them or kill them, whatever floats your boat.
I like the idea of logging; however, I was reading some other posts and was sure I saw something about preventing it (not necessarily permanently stopping them, but rather delaying them and preventing the less-experienced exploiters). I’ll hold off on the solution for now, I’ll mark it later if noone comes up with anything.
Every exploit prevention requires a different solution. In your case your best protection would probably be making a cap of the maximum registered click rate. Go for like 10 - 15 clicks per second - anyone clicking faster than that wouldn’t even notice this cap much.
local click_budget = 15
local function CanClick()
if click_budget > 0 then
click_budget = click_budget - 1
spawn(function()
wait(1)
click_budget = click_budget + 1
end)
return true
else
return false
end
end
This kind of code should be run server-side, individual budget values for each player.
Yeah I considered doing this but I feel like there’d be issues with the client and server boundaries being slightly delayed. I’ll probably go ahead and implement it anyway as there’s no such thing as too much security.
local LogService = game:GetService("LogService")
LogService.MessageOut:connect(function(String,Type)
if Type == Enum.MessageType.MessageOutput and string.find(string.lower(String),"remotespy") ~= nil then
--kick crash or whatever floats your boat when remote spy is found
end
end)
This would be a local script it’d be best to hide it in nil
Does that really work? Would some exploiters really have the audacity to include “remotespy” in any of their scripts? Sorry if I’m wrong here I have no clue when it comes to this, a lot of this is new to me and I plan on using it to better myself.
Well the most common remote spy script prints “remotespy” when it finds a remote being called you can always change the check to find like remote names being printed like if BuyItem is printed out in the dev console you can kick them or whatever
Wouldn’t hiding a script in nil still be visible for exploiters?
I personally prefer the method of checking if the script still exists with the use of remote functions, pcalls, and delays to keep things running smoothly.
Don’t waste time on hacky solutions to detect exploits. An exploiter can just change the output not to include whatever keyword you’re looking for, and they can go about as they please.
These are wastes of your time:
Scanning output for keywords related to exploits
Scanning for certain objects to be present in certain locations to detect exploits
Encrypting remote traffic
Using random numbers as keys for remotes
Hiding your scripts/remotes in weird locations at run-time
Renaming remotes/objects to random strings
All of the above are security through obscurity and can be easily overturned because the exploiter has full control of their own client.
Server-sided checks stop all kinds of exploiters and are the only thing that give you real security, because these checks run on the server which the exploiter cannot touch.
There’s no free lunch. Implement proper server-sided checks, it’s the only way to truly prevent exploiting.
Sure thing, lemme grab an example from one of the games I’m working on…
Local script named “Player Set Up”:
Naming things as plainly as possible to confuse any snoopers
Code
--scripted by GreekForge
local access = game.ReplicatedStorage.Remotes.ClientAccess --most important this is a RemoteFunction
local coms = game.ReplicatedStorage.Remotes.ClientEvent
local plr = game.Players.LocalPlayer
local char = workspace:WaitForChild(plr.Name)
local hum = char:WaitForChild("Humanoid")
local noUse = { --for the rest of my script
Enum.HumanoidStateType.Flying,
Enum.HumanoidStateType.None,
Enum.HumanoidStateType.PlatformStanding,
Enum.HumanoidStateType.Swimming,
}
local function oof()
return true --this can be anything, just return something
end
access.OnClientInvoke = oof
--insert the rest of what you need
Server Script (Can be named anything):
The exploiter cannot do anything to change the server, it only has full access to client
Code
local function playerClientScan(v)--v is player
--check if the client script still exist
wait(0.1) --dunno why
local succ, x;
spawn(function()--make sure the thread doesn't stop
succ, x = pcall(function() return access:InvokeClient(v) end)
end)
delay(9, function() --once again, no stopping the thread
if not succ or x == nil then
spawn(function() succ, x = pcall(function() return access:InvokeClient(v) end) end) --yeah...
delay(3, function()
if not succ or x == nil then --just in case of slow loading
succ, x = pcall(function() return access:InvokeClient(v) end)
end
end)
end
end)
end
spawn(function() --main scan
wait(scanDelay)
while wait(scanDelay) do --added a wait at beginning because this runs once before actually waiting
for _, v in ipairs(game.Players:GetChildren()) do
local char = workspace:WaitForChild(v.Name, 5)
if v then
playerClientScan(v)
slowPositionCheck(v)
end
end
end
end)
Whenever creating an anti-exploit with multiple checks, always make sure that the player hasn’t been kicked or removed before doing the check.
Of course, if you really want to obfuscation on client scripts can severely impair any exploiter.