
Roblox Security
- 444 installs
- 11 repo stars
- Updated February 23, 2026
- sentinelcore/roblox-skills
roblox-security is an agent skill that helps write exploit-resistant Roblox game scripts by enforcing server-side validation and anti-cheat patterns.
About
roblox-security is a Claude Code skill for writing exploit-resistant Roblox game scripts. It teaches the never-trust-the-client rule and shows how to keep authoritative logic (damage, currency, stats, position) on the server, validate every RemoteEvent argument, and add rate limiting, cooldowns, distance checks, and speed-based anti-cheat detection. A developer reaches for it when handling player actions or RemoteEvent/RemoteFunction communication, or when reviewing Roblox code for exploitable patterns.
- Server-side validation patterns for RemoteEvents
- Anti-cheat rate limiting and cooldown code
- Argument-validation utility module for Luau
Roblox Security by the numbers
- 444 all-time installs (skills.sh)
- +30 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #525 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
roblox-security capabilities & compatibility
free, no API key
- Capabilities
- security audit · input validation · rate limiting · anti cheat
- Use cases
- security audit · code review
- Pricing
- Free
What roblox-security says it does
Never trust the client.
Use when writing Roblox game scripts that handle player actions, currencies, stats, damage
npx skills add https://github.com/sentinelcore/roblox-skills --skill roblox-securityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 444 |
|---|---|
| repo stars | ★ 11 |
| Last updated | February 23, 2026 |
| Repository | sentinelcore/roblox-skills ↗ |
How do I stop Roblox exploiters from modifying LocalScripts to award themselves currency, damage, or stats?
security-audit
Who is it for?
Roblox developers writing scripts that handle currencies, damage, stats, or RemoteEvent communication.
Skip if: General web-app security, cloud infrastructure, or non-Roblox game engines.
When should I use this skill?
Writing Roblox scripts that handle player actions, currencies, stats, damage, or any RemoteEvent/RemoteFunction communication, or reviewing code for exploitable patterns.
What you get
Game logic runs authoritatively on the server with validated client requests, rate limiting, and anti-cheat detection.
- Server-side validation scripts
- Rate-limited RemoteEvent handlers
By the numbers
- 6-row secure-vs-insecure pattern table
Files
Roblox Security: Anti-Exploit & Server-Side Validation
Core Principle
Never trust the client. Every LocalScript runs on the player's machine and can be modified. All authoritative logic — damage, currency, stats, position changes — must live on the server.
FilteringEnabled is always on in modern Roblox. Client-side changes do not replicate to the server or other clients unless the server explicitly applies them.
---
Secure vs Insecure Patterns
| Pattern | Insecure | Secure |
|---|---|---|
| Dealing damage | LocalScript sets Humanoid.Health | Server reduces health after validation |
| Awarding currency | LocalScript increments leaderstats | Server validates action, then increments |
| Leaderstats ownership | LocalScript owns the IntValue | Server creates and owns all leaderstats |
| Position changes | LocalScript teleports character | Server validates and moves character |
| Tool use | Client fires damage on hit | Server raycasts and applies damage |
| Cooldowns | Client tracks cooldown locally | Server tracks cooldown per player |
---
Secure Leaderstats Setup
-- Script in ServerScriptService — never LocalScript
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Value = 0
coins.Parent = leaderstats
end)---
Server-Side Sanity Checks
Distance Check
local MAX_INTERACT_DISTANCE = 10
InteractRemote.OnServerEvent:Connect(function(player, targetPart)
if typeof(targetPart) ~= "Instance" or not targetPart:IsA("BasePart") then return end
local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
if not root then return end
if (root.Position - targetPart.Position).Magnitude > MAX_INTERACT_DISTANCE then
warn(player.Name .. " sent interaction from invalid distance")
return
end
processInteraction(player, targetPart)
end)Cooldown Validation
local ABILITY_COOLDOWN = 5
local lastUsed = {}
UseAbilityRemote.OnServerEvent:Connect(function(player)
local now = os.clock()
if now - (lastUsed[player] or 0) < ABILITY_COOLDOWN then return end
lastUsed[player] = now
applyAbility(player)
end)
game.Players.PlayerRemoving:Connect(function(player)
lastUsed[player] = nil
end)Stat Bounds Check
local MAX_QUANTITY = 99
local ITEM_COST = 50
BuyItemRemote.OnServerEvent:Connect(function(player, quantity)
if type(quantity) ~= "number" then return end
quantity = math.clamp(math.floor(quantity), 1, MAX_QUANTITY)
local coins = player.leaderstats.Coins
if coins.Value < ITEM_COST * quantity then return end
coins.Value = coins.Value - (ITEM_COST * quantity)
-- award items server-side
end)---
Rate Limiting
local RATE_LIMIT = 10 -- max calls
local RATE_WINDOW = 1 -- per second
local callLog = {}
local function isRateLimited(player)
local now = os.clock()
local log = callLog[player] or {}
local pruned = {}
for _, t in ipairs(log) do
if now - t < RATE_WINDOW then table.insert(pruned, t) end
end
if #pruned >= RATE_LIMIT then
callLog[player] = pruned
return true
end
table.insert(pruned, now)
callLog[player] = pruned
return false
end
ActionRemote.OnServerEvent:Connect(function(player)
if isRateLimited(player) then return end
handleAction(player)
end)
game.Players.PlayerRemoving:Connect(function(player)
callLog[player] = nil
end)---
Argument Validation Utility
-- ServerScriptService/Modules/Validate.lua
local Validate = {}
function Validate.number(value, min, max)
if type(value) ~= "number" then return false end
if value ~= value then return false end -- NaN check
if min and value < min then return false end
if max and value > max then return false end
return true
end
function Validate.instance(value, className)
if typeof(value) ~= "Instance" then return false end
if className and not value:IsA(className) then return false end
return true
end
function Validate.string(value, maxLength)
if type(value) ~= "string" then return false end
if maxLength and #value > maxLength then return false end
return true
end
return Validate-- Usage
local Validate = require(script.Parent.Modules.Validate)
remote.OnServerEvent:Connect(function(player, amount, targetPart)
if not Validate.number(amount, 1, 100) then return end
if not Validate.instance(targetPart, "BasePart") then return end
-- safe to proceed
end)---
Speed / Anti-Cheat Detection
local SPEED_LIMIT = 32
local violations = {}
task.spawn(function()
while true do
task.wait(2)
for _, player in ipairs(game.Players:GetPlayers()) do
local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
if root and root.AssemblyLinearVelocity.Magnitude > SPEED_LIMIT then
violations[player] = (violations[player] or 0) + 1
if violations[player] >= 3 then
player:Kick("Cheating detected.")
end
else
violations[player] = math.max(0, (violations[player] or 0) - 1)
end
end
end
end)---
ModuleScript Placement
ServerScriptService/
Modules/
DamageCalculator.lua -- server-only, never exposed to client
EconomyManager.lua -- server-only
ReplicatedStorage/
Remotes/ -- RemoteEvent/RemoteFunction instances only
SharedModules/ -- non-sensitive utilities onlyNever put currency, damage, or DataStore logic in ReplicatedStorage modules — clients can require() them.
---
Common Mistakes
| Mistake | Why It's Exploitable | Fix |
|---|---|---|
FireServer(damage) with server trusting it | Client sends any value | Server calculates damage from its own tool data |
| Currency in LocalScript variable | Client can modify memory | Server-owned only |
| Client-side distance check before firing | Check is bypassable | Server re-checks after receiving event |
| No cooldown on RemoteEvent handlers | Spam = infinite resources | Per-player cooldown on server |
Trusting WalkSpeed set by client | Client sets arbitrarily high | Server owns and caps WalkSpeed |
| Sensitive logic in ReplicatedStorage module | Clients can require it | Move to ServerScriptService |
Related skills
How it compares
Use roblox-security for in-experience RemoteEvent hardening; use general appsec skills for non-Roblox web API threat modeling.
FAQ
Why must game logic run on the server?
Every LocalScript runs on the player's machine and can be modified, so all authoritative logic like damage, currency, and stats must live on the server.
Does this cover anti-cheat?
Yes. It includes rate limiting, cooldown validation, stat bounds checks, and speed-based anti-cheat detection patterns.