mcpField
R
roblox-security

roblox-security

github.com/sentinelcore安全29719 次安装35 周前更新
代码仓库

关于 roblox-security

面向 Roblox 开发者的安全技能,介绍服务端验证与反作弊实践,帮助将关键游戏逻辑保留在服务端并减少信任客户端带来的漏洞。

主要功能

  1. 01服务端权威游戏逻辑
  2. 02RemoteEvent 参数验证
  3. 03按玩家设置冷却与速率限制
  4. 04速度与数值合理性检查
  5. 05安全放置 ModuleScript

适用场景

  1. 01验证伤害、货币与物品购买
  2. 02检查远程交互和能力使用
  3. 03审查 Roblox 游戏的客户端与服务端边界

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sentinelcore/roblox-skills --skill roblox-security

安装后在 Claude Code 中输入/roblox-security确认已加载。

详细介绍

Roblox 安全:反外挂与服务端验证

核心原则

永远不要信任客户端。 每个 LocalScript 都在玩家的机器上运行,并且可以被修改。所有权威逻辑——伤害、货币、属性、位置变更——都必须放在服务器上。

现代 Roblox 始终启用 FilteringEnabled。除非服务器明确应用客户端所做的更改,否则这些更改不会复制到服务器或其他客户端。


安全与不安全模式

模式不安全安全
造成伤害LocalScript 设置 Humanoid.Health服务器验证后降低生命值
发放货币LocalScript 增加 leaderstats服务器验证操作后再增加
Leaderstats 所有权LocalScript 拥有 IntValue服务器创建并拥有所有 leaderstats
位置变更LocalScript 传送角色服务器验证并移动角色
使用工具客户端命中时触发伤害服务器执行射线检测并造成伤害
冷却时间客户端在本地跟踪冷却时间服务器按玩家跟踪冷却时间

安全的 Leaderstats 设置

-- 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)

服务端合理性检查

距离检查
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)
冷却时间验证
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)
属性范围检查
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)

速率限制

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)

参数验证工具

-- 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)

速度/反作弊检测

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 的放置位置

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

绝不要将货币、伤害或 DataStore 逻辑放在 ReplicatedStorage 模块中——客户端可以 require() 这些模块。


常见错误

错误可被利用的原因修复方法
使用 FireServer(damage) 且服务器信任该值客户端可以发送任意值服务器根据自己的工具数据计算伤害
在 LocalScript 变量中保存货币客户端可以修改内存仅由服务器拥有
触发前在客户端检查距离该检查可以被绕过服务器收到事件后重新检查
RemoteEvent 处理程序没有冷却时间垃圾请求 = 无限资源服务器为每位玩家设置冷却时间
信任由客户端设置的 WalkSpeed客户端可以将其设为任意高的值由服务器控制并限制 WalkSpeed
在 ReplicatedStorage 模块中放置敏感逻辑客户端可以 require 该模块移至 ServerScriptService

评论

支持 Markdown