mcpField
R
roblox-security

roblox-security

github.com/el4cteo安全56537 次安装2 周前更新
代码仓库

关于 roblox-security

roblox-security 是面向 Roblox 开发者的游戏安全技能,重点介绍服务器端设计。内容涵盖参数校验、限流、移动与战斗检查、经济系统保护及违规检测。

主要功能

  1. 01服务器端校验与权威控制
  2. 02限制客户端触发操作的频率
  3. 03安全处理提示与触碰事件
  4. 04验证移动与战斗行为
  5. 05保护经济系统与购买流程
  6. 06违规检测与处置建议

适用场景

  1. 01审查 RemoteEvent 和 RemoteFunction 处理逻辑
  2. 02加固战斗与移动系统
  3. 03保护交易和游戏内购买

安装

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

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

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

附带文件references/checklist.md

详细介绍

Roblox 安全

攻击者完全控制自己的客户端:他们可以运行任意 Luau、读取所有已复制的脚本 (反编译后)、以任意参数和频率触发任何远程事件、修改任意本地属性(WalkSpeed、 提示词 Enabled、MaxActivationDistance),并移动他们拥有网络所有权的任何对象。 客户端反作弊充其量只能稍微增加攻击难度。安全性取决于服务端设计。

核心规则

  1. 由服务端决定。 客户端发送意图(“我按下了攻击键,目标瞄准这里”),而不是结果 (“我造成了 50 点伤害”“给我 100 个金币”)。
  2. 验证每个 OnServerEvent/OnServerInvoke 的每个参数:类型、结构、范围和有限性。
  3. 验证上下文:玩家是否存活、是否在范围内、是否处于冷却状态、是否拥有该物品、是否允许执行操作?
  4. 对客户端可触发的一切操作进行速率限制(远程事件、提示、触碰、点击)。
  5. 将机密保留在服务端:服务端代码放在 ServerScriptService/ServerStorage 中;通过 HttpService:GetSecret() 获取 API 密钥;绝不将尚未发布的内容放入生产环境。
  6. 优先采用静默缓解措施(丢弃、限制、纠正状态),而非立即踢出;对重复出现的信号逐步升级处置。

参数验证工具包

--!strict
-- ServerScriptService/Server/Validate.luau
local Validate = {}

function Validate.number(value: unknown, min: number, max: number): number?
	if typeof(value) ~= "number" or not math.isfinite(value) then
		return nil -- rejects NaN and ±inf (NaN fails every comparison, so range checks alone don't catch it)
	end
	if value < min or value > max then
		return nil
	end
	return value
end

function Validate.integer(value: unknown, min: number, max: number): number?
	local n = Validate.number(value, min, max)
	return if n and n == math.floor(n) then n else nil
end

function Validate.string(value: unknown, maxLength: number): string?
	if typeof(value) ~= "string" or #value > maxLength or utf8.len(value) == nil then
		return nil -- also rejects invalid UTF-8 (which DataStores refuse)
	end
	return value
end

function Validate.vector3(value: unknown, maxMagnitude: number): Vector3?
	if typeof(value) ~= "Vector3" then
		return nil
	end
	if not (math.isfinite(value.X) and math.isfinite(value.Y) and math.isfinite(value.Z)) then
		return nil
	end
	return if value.Magnitude <= maxMagnitude then value else nil
end

-- Accept only a real instance of the expected class that lives under a trusted container.
-- Exploiters can send tables shaped like instances ({ ClassName = "Part", ... }).
function Validate.instanceIn(value: unknown, className: string, container: Instance): Instance?
	if typeof(value) ~= "Instance" or not value:IsA(className) or not value:IsDescendantOf(container) then
		return nil
	end
	return value
end

function Validate.enum<T>(value: unknown, allowed: { [T]: boolean }): T?
	if allowed[value :: any] then
		return value :: any
	end
	return nil
end

return Validate
  • 将 player 之后传入处理程序的每个参数都视为 unknown,并用这些辅助函数缩小其类型范围。
  • 根据 ID 在服务端表中查找物品(ITEMS[itemId]);绝不从客户端数据或客户端可以引用的已复制 NumberValue 中读取价格或属性。
  • 限制表的大小,并防御性地遍历客户端表(键可能类似 NaN、嵌套或过大)。

速率限制(令牌桶)

--!strict
local Players = game:GetService("Players")

type Bucket = { tokens: number, last: number }

local function newLimiter(capacity: number, windowSeconds: number)
	local refillRate = capacity / windowSeconds
	local buckets: { [Player]: Bucket } = {}

	Players.PlayerRemoving:Connect(function(player)
		buckets[player] = nil
	end)

	return function(player: Player): boolean
		local now = os.clock()
		local bucket = buckets[player]
		if not bucket then
			bucket = { tokens = capacity, last = now }
			buckets[player] = bucket
		else
			bucket.tokens = math.min(capacity, bucket.tokens + (now - bucket.last) * refillRate)
			bucket.last = now
		end
		if bucket.tokens >= 1 then
			bucket.tokens -= 1
			return true
		end
		return false
	end
end

local allowAttack = newLimiter(5, 1) -- bursts of 5, refills 5/second
print(allowAttack)

特别要对以下昂贵或易遭利用的操作进行速率限制:DataStore/Badge/HTTP 调用(它们各自有调用额度)、克隆大型模型、传送、发放货币,以及向所有客户端广播的任何操作。

客户端触发的实例并不安全

对象攻击者可以做什么服务端必须做什么
ProximityPrompt即使禁用也能触发事件;在本地更改距离、视线检测和 HoldDuration。只有 Triggered 会检查距离。重新检查启用状态、与角色的距离、存活状态和冷却状态。
ClickDetector即使禁用,也能从任意距离触发。服务端完全不做检查。同上。
DragDetector继承的点击事件未经检查;拖动只受 Enabled 和 PermissionPolicy 限制。在服务端验证拖动结果。
客户端拥有的部件上的 Touched伪造或抑制触碰事件。使用服务端的距离/重叠查询进行验证。

如果这些对象位于客户端可以拥有的未锚定部件中,攻击者就能将部件拉到自己身边:将部件锚定,或使用 part:SetNetworkOwner(nil) 将所有权设为服务端。

网络所有权和移动

客户端拥有其角色的物理模拟权,因此可以传送、飞行、穿墙、加速作弊、将其他玩家甩飞(使用 NaN/超大速度),并抑制 Touched。以下方案按效果从强到弱排列:

  1. 服务端权威控制(Workspace AuthorityMode = Server;已完全发布,可选择启用):服务端使用客户端预测和回滚来模拟移动,从而消除移动作弊。 参见 roblox-physics。
  2. 服务端移动检查:按时间间隔将水平(XZ)位移与最大速度及延迟容差进行比较;对合法传送进行豁免(由你移动玩家时,在服务端设置标记);将玩家回弹到最后一个有效位置,而非踢出。
  3. 让游戏玩法关键的未锚定部件由服务端拥有:part:SetNetworkOwner(nil)。

战斗验证(命中报告)

客户端发送起点、方向/目标部件和时间戳。服务端检查:

  • 起点是否接近角色的服务端位置(根据 player:GetNetworkPing() 调整容差)。
  • 目标是否存活、是否为敌人、是否在武器射程内;声称的命中点是否接近目标的服务端位置。
  • 对静态几何体进行服务端射线检测,确认起点与目标之间没有墙壁。
  • 射速、弹药、装填/冲刺状态均由服务端跟踪。

经济、交易、购买

  • 货币、库存和价格只存在于服务端。客户端发起请求;服务端检查余额并以原子方式应用更改。
  • 交易:完整验证双方,然后提交双方,然后保存双方;任何失败都回滚一切。警惕“交易后在保存前离开”导致的复制漏洞:交易期间锁定物品,并在交易后立即保存。
  • Robux 购买:仅在 MarketplaceService.ProcessReceipt(开发者产品)中发放,或在服务端所有权检查(通行证)之后发放。绝不要信任来自客户端的 PromptProductPurchaseFinished。参见 roblox-monetization。

保密性

  • 任何会复制的 LocalScript、客户端 Script 或 ModuleScript 都可能被反编译——即使已禁用或从未被 require。不要把仅服务端分支混入共享模块。
  • 任何会被复制的内容(模型、UI、属性)都可能被数据挖掘。将未发布内容放在独立的私有 universe 中,而不是隐藏在生产环境中。
  • 客户端可以传送到 universe 中的任何 place:在 Creator Dashboard 中设置 Access Control for Places → Secure within universe only,并在受限 place 的加入时验证资格(默认拒绝)。
  • 绝不要把 API 密钥放在脚本或属性中;使用体验内 Secrets 存储(HttpService:GetSecret)。

检测与后果

在验证之上叠加服务端启发式检测:最快可能的完成时间、资源获取速率、非人类般规律的操作节奏、正常客户端不会触发的蜜罐 remote。累积怀疑分数;基于多个信号采取行动。阶梯:静默记录 → 静默缓解 → 限制功能 → 踢出/封禁。延迟可见后果,使作弊者无法二分探测你的检查。

--!strict
local Players = game:GetService("Players")

local function banForExploiting(player: Player, evidence: string)
	local ok, err = pcall(function()
		Players:BanAsync({
			UserIds = { player.UserId },
			Duration = 7 * 24 * 60 * 60, -- seconds; -1 = permanent
			DisplayReason = "Exploiting detected.", -- shown to the user (max 400 chars, filtered)
			PrivateReason = evidence, -- visible to you only
			ExcludeAltAccounts = false, -- also ban detected alt accounts
			ApplyToUniverse = true,
		})
	end)
	if not ok then
		warn("BanAsync failed:", err)
		player:Kick("Exploiting detected.")
	end
end

return banForExploiting

为 Ban API 启用 Players.BanningEnabled。优先使用可逆、有时限的封禁(检查 Players:GetBanHistoryAsync 以升级);对检测规则进行版本管理,并监控误报率。

安全审查清单和常见易受攻击代码模式:references/checklist.md。

相关技能

roblox-networking、roblox-physics(所有权、服务端权威)、roblox-data-stores、roblox-monetization、roblox-code-review。

评论

支持 Markdown