了解客户端可见的信息和资产对于维护游戏的安全性和机密性至关重要。开发者常常低估了可被利用者看到和复制到客户端的内容。利用者如果能够加入一个地方,就可以访问您宇宙中的“受限”地点。他们可以查看任何复制到其客户端的内容,无论该内容是否可见或当前是否在使用。此外,利用者可以反编译任何复制的本地脚本和模块脚本,即使这些脚本从未在客户端上执行。
宇宙内的客户端传送
一旦进入宇宙,客户端可以传送到该宇宙内的任何地方,可能会绕过任何预期的访问限制或进度门。这也可能导致未发布内容的意外泄露,例如来自开发或暂存游戏的内容。重要的是要理解:
- 禁用“直接访问”仅会从网站上的子地方移除加入按钮——这并不阻止客户端发起的传送
- 假设利用者会发现您宇宙中所有子地方的存在
- 如果客户端能够访问一个地方,例如根地方,则可以传送到任何子地方,无论您的设计流程如何
许多开发者试图通过将未授权用户踢出子地方来防止访问。这可能在阻止游戏玩法方面有效,但并不防止内容复制到客户端,因为复制在玩家加入后立即开始,而在服务器端踢出逻辑执行之前。
使用安全传送
防止未授权访问的最有效方法是在创作者仪表板中将地方的访问控制设置为仅在宇宙内安全。这将限制所有非起始地方仅允许服务器发起的传送,在玩家加入子地方之前,在平台级别阻止客户端发起的传送。由于玩家从未加入,因此没有内容复制到他们。
有关配置步骤和现有游戏迁移指南(使用客户端发起的传送),请参见在地方之间传送。
受限地方的深度防御
对于无法使用安全传送的宇宙,或作为其附加保护层,结合以下措施:
- 将开发和测试地方保存在单独的私有宇宙中——这是确保未发布内容机密性的唯一可靠方法。在未准备好让其在生产环境中激活之前,绝不要将机密事件资产、脚本或 UI 元素发布到生产环境中。
- 如果可行,使用流式传输来限制新玩家复制的世界内容量。
- 添加服务器端组角色或徽章验证,并验证玩家状态或进度要求。
- 默认情况下,禁止新玩家进入。这确保即使验证过程不确定,例如引擎抛出异常,也不会允许任何玩家进入。
- 如果实际可行,使用 Ban API 对未通过验证的玩家进行封禁。这防止玩家在同一账户上不断尝试加入。
复制
复制描述了状态如何在引擎实例之间通过网络传输。Roblox 的复制模型在几个关键方面通常是简化的:
- 实例是服务器授权的,这意味着为了在所有参与者(服务器和所有连接的客户端)之间复制,实例必须在服务器上创建。
- 实例属性也是服务器授权的,这意味着大多数属性必须在服务器上更改,以便更改在所有客户端上可见。
- 一般来说,实例要么复制到所有连接的客户端,要么不复制。有一些例外,例如流式传输。
复制容器是顶级实例(在 DataModel 下)复制到客户端。如果一个实例在其生命周期内成为复制容器的后代,您应该期望其状态的大部分复制到所有客户端。您可以在数据模型指南中阅读有关常见复制容器的更多信息。当有疑问时,您可以随时检查实例或属性在测试环境(例如单人游戏)中的复制方式。您可以在Studio 测试模式中阅读有关测试模式的更多信息。
复制的安全影响
任何复制到客户端的内容都可以被利用者提取和数据挖掘。一般来说,避免在实时生产环境中发布或运输任何机密内容,除非您已准备好让用户立即看到它。即使在游戏中有逻辑限制内容的发布或访问,直到稍后的日期(或其他条件),也要假设利用者会找到方法来发现和泄露您的内容,一旦它被发布。
避免使用过于描述性或可预测的名称来命名敏感实例,包括但不限于:脚本、远程和模型。拥有可预测的数据模型层次结构使得开发利用更容易。
脚本反编译
任何 LocalScript、Script(具有 RunContext::Client)或 ModuleScript 一旦复制到其客户端,就可以被利用者反编译,即使这些脚本被禁用、从未被要求或从未在客户端上运行。存储在 ServerStorage 或 ServerScriptService 中的仅服务器脚本和模块脚本无法被反编译,因为它们从未复制到客户端。
在同一脚本中编写包含仅服务器和仅客户端代码的模块脚本是不明智的,因为服务器端逻辑将在反编译中暴露,并且更容易被分析以发现可以被利用的漏洞。
local module = {}
local Remote = script:WaitForChild("RemoteEvent")
-- 这是一个需要避免的范例
-- 将服务器代码保留在脚本实例或 ServerStorage/ServerScriptStorage 中!
if game:GetService("RunService"):IsServer() then
function module:DoServerThing(password)
if password == "SecretPassword" then
print("敏感的服务器代码")
end
end
Remote.OnServerEvent:Connect(function(player, password)
module:DoServerThing(password)
end)
else
function module:DoServerThing(password)
Remote:FireServer(password)
end
end
return modulelocal table1 = {};
local RemoteEvent = script:WaitForChild("RemoteEvent");
if game:GetService("RunService"):IsServer() then
function table1.DoServerThing(_, p2) -- 行:9
if p2 == "SecretPassword" then
print("敏感的服务器代码");
end
end
RemoteEvent.OnServerEvent:Connect(function(player: Player, p3) -- 行:15
--[[
上值:
[1] = table1
--]]
table1:DoServerThing(p3);
end);
return table1;
end
function table1.DoServerThing(_, p1) -- 行:19
--[[
上值:
[1] = RemoteEvent
--]]
RemoteEvent:FireServer(p1);
end
return table1;如上所示,反编译揭示了服务器端逻辑,包括硬编码的密码、业务规则和实现细节,利用者可以分析这些内容以寻找漏洞。
机密性泄露的影响
机密性泄露可能会产生重大后果,例如:
- 内容泄露:未发布的物品、地图或功能可能在官方公告之前被发现并公开分享
- 竞争劣势:竞争对手可能会逆向工程机制、算法或即将推出的功能
- 利用开发:暴露的服务器逻辑使得利用者更快、更容易识别漏洞并开发针对性的攻击