How to check Instances sent through RemoteEvents
Checking an Instance's class doesn't prove the player may use it. How to check a tool, part or model sent by the client belongs to that player.
What it looks like
The client sends an Instance, such as a tool, a part, a model or a player, and the server acts on it after checking only its type or class. Nothing checks that the player is allowed to touch that particular instance.
Luau
remote.OnServerEvent:Connect(function(player, tool) if typeof(tool) == "Instance" and tool:IsA("Tool") then tool.Parent = workspace end end)
How to detect it
- Find client arguments that are used as Instances: properties set on them, methods called on them (
Destroy,Clone,PivotTo,SetAttribute), or reparenting. - Check for
typeof(x) == "Instance"first, thenIsAfor the expected class, then an ownership or reach check: the instance is inside the player's ownBackpackorCharacter, belongs to their plot, or is within a sensible distance. IsAalone is not ownership. Every tool in the game passesIsA("Tool").- A Player argument is an Instance too. When a handler takes a target player, check
IsA("Player")and decide whether the player may act on that target, includingtarget ~= playerwhere acting on yourself makes no sense.
What an exploiter could do
The client can send a reference to any instance it can see that also exists on the server, including other players' characters and the tools they're holding, plus parts of the map. If the server moves, deletes, clones or edits whatever it's given, the exploiter can do the same to things that aren't theirs: take tools out of other players' hands, delete map parts, or duplicate items.
How to fix it
- Check
typeofandIsAfirst, then check ownership against something the server controls, such astool.Parent == player:FindFirstChildOfClass("Backpack")ortool:IsDescendantOf(player.Character). - Where you can, have the client send a name or id instead and find the instance on the server inside the player's own containers.
- If any player may use the instance but only when close to it, such as a chest or a door, the check you need is distance. That's World interaction with no distance check in server validation.
Vulnerable example
Vulnerable
local ReplicatedStorage = game:GetService("ReplicatedStorage") ReplicatedStorage.Remotes.DropTool.OnServerEvent:Connect(function(player: Player, tool: unknown) if typeof(tool) ~= "Instance" or not tool:IsA("Tool") then return end tool.Parent = workspace end)
Fixed example
Fixed
local ReplicatedStorage = game:GetService("ReplicatedStorage") ReplicatedStorage.Remotes.DropTool.OnServerEvent:Connect(function(player: Player, tool: unknown) if typeof(tool) ~= "Instance" or not tool:IsA("Tool") then return end local character = player.Character if not character or tool.Parent ~= character then return end tool.Parent = workspace end)
Common false positives
- Instances the server finds itself, such as
player.Characterorplayer:FindFirstChildOfClass("Backpack"), are not client input. - An Instance argument that is only compared, like
if part == door then, and never acted on is safe. - Instances that only exist on the client arrive on the server as
nil, so a handler that returns early onnilhandles them.