Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
sentinelcore avatar

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)
At a glance

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
From the docs

What roblox-security says it does

Never trust the client.
SKILL.md
Use when writing Roblox game scripts that handle player actions, currencies, stats, damage
SKILL.md
npx skills add https://github.com/sentinelcore/roblox-skills --skill roblox-security

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs444
repo stars11
Last updatedFebruary 23, 2026
Repositorysentinelcore/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

SKILL.mdMarkdownGitHub ↗

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

PatternInsecureSecure
Dealing damageLocalScript sets Humanoid.HealthServer reduces health after validation
Awarding currencyLocalScript increments leaderstatsServer validates action, then increments
Leaderstats ownershipLocalScript owns the IntValueServer creates and owns all leaderstats
Position changesLocalScript teleports characterServer validates and moves character
Tool useClient fires damage on hitServer raycasts and applies damage
CooldownsClient tracks cooldown locallyServer 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 only

Never put currency, damage, or DataStore logic in ReplicatedStorage modules — clients can require() them.

---

Common Mistakes

MistakeWhy It's ExploitableFix
FireServer(damage) with server trusting itClient sends any valueServer calculates damage from its own tool data
Currency in LocalScript variableClient can modify memoryServer-owned only
Client-side distance check before firingCheck is bypassableServer re-checks after receiving event
No cooldown on RemoteEvent handlersSpam = infinite resourcesPer-player cooldown on server
Trusting WalkSpeed set by clientClient sets arbitrarily highServer owns and caps WalkSpeed
Sensitive logic in ReplicatedStorage moduleClients can require itMove 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.

Securityappsecaudit

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.