How to stop players dragging other players' things

DragDetectors let everybody drag by default. How to use a Scriptable permission policy that checks ownership on the server.

warningLuaudit check: unrestricted-drag-detectors

What it looks like

A DragDetector lets players move something that belongs to someone or matters to the game, such as furniture on a plot, a chess piece, a puzzle block or a shop display. Its PermissionPolicy is left at the default, Everybody, so any player can drag it.

Luau
local detector = Instance.new("DragDetector")
detector.Parent = furniture

How to detect it

  • Find every DragDetector, in scripts with Instance.new("DragDetector") and in the project or place if they're visible, and work out what each one moves.
  • Roblox's drag detector guide says PermissionPolicy defaults to Everybody. Scriptable uses a function registered with SetPermissionPolicyFunction to decide per player, and with no function, or an invalid result, nobody can drag.
  • Roblox's security guide says that for drag events the server checks only Enabled and PermissionPolicy. It doesn't check distance or any other property, so MaxActivationDistance doesn't stop a client dragging from anywhere.
  • For player-owned objects, check that the permission function compares the player with the owner the server recorded, not an attribute or name the client could influence.
  • For turn-based or timed objects, check the permission function reads server-side state, like whose turn it is.
  • With RunLocally set to true, nothing is sent to the server, so the game must use remotes to save the result. Judge those remotes with the usual checks, such as Placement the server doesn't keep inside the player's area.
  • Watch for DragDetector on unanchored parts. Players can also move those through network ownership. See Rewards decided by parts a client simulates.

What an exploiter could do

They drag other players' furniture off their plots, move pieces in a game they're not playing, solve puzzles for others or shift shop displays, from anywhere on the map. Where positions are saved, the damage stays after they leave.

How to fix it

  • Set PermissionPolicy to Scriptable and register a function that checks the player against server-side state, such as the plot's recorded owner or whose turn it is.
  • Set Enabled to false on the server when an object shouldn't be dragged at all.
  • When the dragged position is saved, check the final position on the server before saving it.

Vulnerable example

Vulnerable
local Workspace = game:GetService("Workspace")

local plots = Workspace:WaitForChild("Plots")

local function makeDraggable(item: Model)
	local detector = Instance.new("DragDetector")
	detector.DragStyle = Enum.DragDetectorDragStyle.TranslatePlane
	detector.MaxActivationDistance = 20
	detector.Parent = item
end

for _, plot in plots:GetChildren() do
	plot.ChildAdded:Connect(function(child)
		if child:IsA("Model") then
			makeDraggable(child)
		end
	end)
end

Fixed example

Fixed
local Workspace = game:GetService("Workspace")

local plots = Workspace:WaitForChild("Plots")
local owners: { [Instance]: Player } = {}

local function makeDraggable(item: Model, plot: Instance)
	local detector = Instance.new("DragDetector")
	detector.DragStyle = Enum.DragDetectorDragStyle.TranslatePlane
	detector.MaxActivationDistance = 20
	detector.PermissionPolicy = Enum.DragDetectorPermissionPolicy.Scriptable
	detector:SetPermissionPolicyFunction(function(player: Player)
		return owners[plot] == player
	end)
	detector.Parent = item
end

for _, plot in plots:GetChildren() do
	plot.ChildAdded:Connect(function(child)
		if child:IsA("Model") then
			makeDraggable(child, plot)
		end
	end)
end

The owners table is filled by the server code that hands out plots.

Common false positives

  • Toys and props anyone is meant to play with, such as a ball in a lobby, can use Everybody.
  • Drag detectors used only for local interface or previews, with RunLocally set and nothing saved, don't affect other players.
  • Scriptable with a function that checks server-side ownership, like the fixed example, is the fix.