2026年5月20日
5 min read
ZeroTrust Team

保护您的事件:FiveM Lua 事件安全指南

作弊允许恶意客户端在任何上下文中触发事件。了解如何保护客户端-服务器通信、实现服务器端验证以及配置 FXServer 安全 Convars。

反作弊团队一直在努力改进反作弊系统,但有时还是会有漏洞。

在本指南中,我们将尝试帮助您介绍一些常见做法,您可以通过正确锁定事件来使服务器更加安全。

了解 FiveM 中的网络事件

作弊可以允许客户端在任何上下文中触发事件。

当我们说上下文时,是指他们可以执行客户端->服务器(通过 TriggerServerEvent)或客户端资源->客户端资源(通过 TriggerEvent)。

在 Lua 中正确使用事件处理程序

在 Lua 中处理事件时,根据事件是由客户端还是服务器调用来正确注册它们至关重要。

一个常见的错误是注册了不应该被客户端调用的服务器事件,反之亦然,这可能会导致严重的安全漏洞。

AddEventHandler

当事件旨在在相同上下文(客户端-客户端或服务器-服务器)内触发时,请使用 AddEventHandler。这可以确保事件不联网,并且不能被对立面调用。

AddEventHandler("eventName", function(eventParam1, eventParam2)
    -- 一旦在相同上下文中触发事件,此处代码将被执行。
end)

RegisterNetEvent

当事件需要跨不同上下文触发时(例如从客户端到服务器,或从服务器到客户端),请使用 RegisterNetEvent

技术内幕

注意:这不会阻止来自相同上下文的执行。在底层,RegisterNetEvent 是一个包装器,其实际上只是以下内容: RegisterNetEvent("eventName") AddEventHandler("eventName", function() ... end)
RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- 一旦跨不同上下文触发事件,此处代码将被执行。
end)

此示例针对客户端,与客户端上的任何内容一样,它不是万无一失的,并且可以被作弊客户端操纵。

如果您想阻止来自相同上下文的执行(例如防止客户端触发服务器到服务器的网络事件),您应该注册您的事件并验证发件人来源:

RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- 服务器将为来自服务器的事件发送网络 ID `65535`
    if source ~= 65535 then return end
end)

添加检查和验证

即使您构建了强大的反作弊系统,在服务器事件上添加检查也会使其显著更加安全。这是一种强烈推荐的做法,尽管它不能防止所有事情。下面,我们分享一些不错的技巧。

  • 玩家金钱:在服务器端验证余额和交易。
  • 玩家状态包 (State Bags):验证活跃会话中的状态数据。
  • 玩家背包物品:检查物品名称和数量。
  • 玩家位置:验证范围和距离。
  • 玩家经验和等级:确保属性在服务器端计算。
  • 玩家权限和角色:在服务器端验证 ACL。

经验法则

确保使用服务器端方法获取所有值,不允许玩家提供或更改值。请注意,客户端检查对于提升用户体验 (UX) 也是一种很好的做法,但很容易被绕过。

这可以确保您游戏环境的完整性和安全。

常见安全模式示例

以下所有示例均假设使用某种框架(例如 ESX、QB-Core 等)。

不安全做法(千万不要这样做)

这是为了向您展示做事件的不安全方式,您千万不要这样做。直接根据用户自身的输入向其添加物品总是糟糕的做法:您应该始终验证用户输入。

RegisterNetEvent("job:givePlayerItem", function(item, count)
    local ply = FX.GetPlayerFromSource(source)
    -- 直接根据用户自身的输入向其添加物品是不安全的!
    ply.addItem(item, count)
end)

安全做法(推荐)

这是一个强大的安全模式。服务器追踪玩家状态和坐标,并使用服务器端 Tick 验证操作,而不是仅仅依赖客户端触发器。

-- 随机坐标
local VALID_JOB_COORD = vector3(125.0, 111.1, 35.83)
local MAX_ITEM_COUNT = 10

-- 随机坐标
local VALID_TURNIN_COORD = vector3(1888.0, 1254.1, 48.0)

local ITEM_NAME = 'log'

-- 具有活跃工作的玩家列表
local activeJobs = {}

AddEventHandler("playerDropped", function ()
    if not activeJobs[source] then return end
    activeJobs[source] = nil
end)

function isPedWithinRange(ped, tgtCoords)
    return #(GetEntityCoords(ped) - tgtCoords) < 15.0
end

-- 在服务器端处理增加玩家获得的物品数量
CreateThread(function()
    while true do
        for src, data in pairs(activeJobs) do
            local ped = GetPlayerPed(src)
            -- 如果他们不在范围内,我们就不想给他们物品
            if isPedWithinRange(ped, VALID_JOB_COORD) then
                -- 给他们物品,但限制为 MAX_ITEM_COUNT
                data.itemCount = math.min(data.itemCount + 1, MAX_ITEM_COUNT)
            end
        end
        -- 每秒处理一次工作 tick
        Wait(1000)
    end
end)

RegisterNetEvent("job:startJob", function()
    local ped = GetPlayerPed(source)
    -- 如果他们在 15 个单位内,他们就在做这项工作
    if isPedWithinRange(ped, VALID_JOB_COORD) then
        activeJobs[source] = {
            itemCount = 0,
        }
    end
end)

RegisterNetEvent("job:givePlayerItem", function()
    local ply = FX.GetPlayerFromSource(source)
    -- 如果他们没有活跃的工作,他们就不应该触发这个!
    local jobData = activeJobs[source]
    if not jobData then return end

    local ped = GetPlayerPed(source)
    -- 他们不在交件坐标范围内,拒绝其更改
    if not isPedWithinRange(ped, VALID_TURNIN_COORD) then return end

    -- 重置物品数据,以便他们无法多次触发它
    activeJobs[source] = nil

    -- 将经验证的物品添加到用户,在服务器端验证
    ply.addItem(ITEM_NAME, jobData.itemCount)
end)

服务器管理员选项 (Convars)

请注意,除非您确切知道自己在做什么,否则不应修改以下设置。Cfx.re / Adhesive 团队一直在非常努力地防范作弊者。在 FXServer 8450 构建版本及更高版本中,您将默认启用大多数这些功能。

  • sv_kick_players_cnl_timeout_sec:服务器踢出玩家的超时时间(例如,如果设置为 600,则在 10 分钟没有 CnL 连接后踢出玩家)。
  • sv_kick_players_cnl_update_rate_sec:使用玩家列表查询 CnL 的频率。
  • sv_pure_verify_client_settings:取代对客户端中 info.json 的定期请求。在 adhesive 和 svadhesive 之间建立安全连接,并验证一些 sv_settings,例如 pureLevel、scripthook 和其他配置。
  • sv_kick_players_cnl_consecutive_failures:在踢出玩家之前,需要在 timeout_sec 内连续看到多少次失败。默认设置为 2,表示如果玩家 10 分钟未能签到,然后错过了下一次签到更新,他们将被踢出。这起到了失效安全机制的作用。
  • sv_authMaxVariance:方差是指对于给定的提供商(即 'steam'、'ip' 或 'license'),用户的 ID 发生变化的可能性有多大。
  • sv_authMinTrust:信任是指用户的身份不太可能被恶意客户端伪造。
  • sv_filterRequestControl:用于基于可配置策略阻止 REQUEST_CONTROL_EVENT 路由的控制台变量。
  • sv_disableClientReplays:启用此项旨在减少作弊选项的机会。请注意,这将禁用 Rockstar 编辑器。

在玩家端的表现

激活这些 Convars 可能会让玩家因以下原因被踢出: Connection to CNL timed out.

被踢出并不意味着玩家被自动全局封禁 (Global Ban)。然而,它提供了玩家可靠性的有力指示,这对于评估信任度非常有用。

重要须知

所提供的代码并不适用于直接复制粘贴使用。这只是一些防止服务器中可能发生的某些行为的提示。这需要一些编程知识。欢迎您随时加入我们的 Discord 以获取更多帮助。