20 de maio de 2026
5 min read
ZeroTrust Team

Proteja seus eventos: Guia de segurança de eventos Lua no FiveM

Cheats permitem que clientes maliciosos acionem eventos em qualquer contexto. Aprenda a proteger a comunicação cliente-servidor, implementar validação no lado do servidor e configurar convars de segurança do FXServer.

A equipe do anti-cheat está sempre tentando melhorar o anti-cheat, mas às vezes algumas coisas passam.

Neste guia, tentaremos ajudar a cobrir algumas práticas comuns que você pode adotar para tornar seu servidor mais seguro, bloqueando adequadamente seus eventos.

Entendendo eventos de rede no FiveM

Cheats podem permitir que o cliente acione eventos em qualquer contexto.

Quando dizemos contexto, queremos dizer que eles podem executar cliente->servidor (via TriggerServerEvent) ou recurso do cliente->recurso do cliente (via TriggerEvent).

Uso correto de manipuladores de eventos em Lua

Ao trabalhar com eventos em Lua, é crucial registrá-los corretamente com base em se eles são chamados pelo cliente ou pelo servidor.

Um erro comum é registrar eventos do servidor que não deveriam ser chamados pelo cliente, ou vice-versa, o que pode levar a graves vulnerabilidades de segurança.

AddEventHandler

Use AddEventHandler quando o evento for destinado a ser acionado no mesmo contexto, seja cliente-cliente ou servidor-servidor. Isso garante que os eventos não sejam transmitidos pela rede e não possam ser chamados pelo lado oposto.

AddEventHandler("eventName", function(eventParam1, eventParam2)
    -- O código aqui será executado assim que o evento for acionado no mesmo contexto.
end)

RegisterNetEvent

Use RegisterNetEvent quando o evento precisar ser acionado em diferentes contextos, como do cliente para o servidor ou do servidor para o cliente.

Por baixo do capô

NOTA: Isso não bloqueia a execução no mesmo contexto. Por baixo do capô, RegisterNetEvent é um wrapper que é simplesmente o seguinte: RegisterNetEvent("eventName") AddEventHandler("eventName", function() ... end)
RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- O código aqui será executado assim que o evento for acionado em diferentes contextos.
end)

Este exemplo é para o cliente e, como qualquer coisa no cliente, não é infalível e pode ser manipulado por clientes trapaceiros.

Se você deseja bloquear a execução no mesmo contexto (como impedir que um evento de rede de servidor para servidor seja acionado pelo cliente), você deve registrar seu evento e verificar a fonte do remetente:

RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- O servidor enviará o ID de rede `65535` para eventos do servidor
    if source ~= 65535 then return end
end)

Adicionando verificações e validação

Mesmo que você construa um anti-cheat forte, adicionar verificações nos eventos do servidor os torna significativamente mais seguros. Esta é uma prática altamente recomendada, embora não evite tudo. Abaixo, compartilhamos algumas boas dicas.

  • Dinheiro do jogador: Valide o saldo e as transações no lado do servidor.
  • State bags do jogador: Verifique os dados de estado nas sessões ativas.
  • Itens do inventário do jogador: Verifique os nomes e as quantidades dos itens.
  • Posição do jogador: Verifique alcances e distâncias.
  • Experiência e nível do jogador: Garanta que as estatísticas sejam calculadas no lado do servidor.
  • Permissões e funções do jogador: Valide a ACL no lado do servidor.

Regra geral

Certifique-se de obter todos os valores usando métodos do lado do servidor, não permitindo que os jogadores forneçam ou alterem os valores. Observe que as verificações no cliente também podem ser uma boa prática para a experiência do usuário (UX), mas podem ser facilmente burladas.

Isso garante a integridade e a segurança do seu ambiente de jogo.

Exemplos de padrões de segurança comuns

Todos os exemplos abaixo assumem algum tipo de framework (como ESX, QB-Core, etc.).

Segurança ruim (Nunca faça isso)

Isso serve para mostrar maneiras incorretas de lidar com eventos. Você nunca deve fazer isso. Adicionar itens diretamente ao usuário a partir de sua própria entrada é sempre uma má prática: você sempre deve validar a entrada do usuário.

RegisterNetEvent("job:givePlayerItem", function(item, count)
    local ply = FX.GetPlayerFromSource(source)
    -- Adicionar itens diretamente ao usuário a partir de sua própria entrada é inseguro!
    ply.addItem(item, count)
end)

Segurança boa (Recomendado)

Aqui está um padrão de segurança robusto. O servidor acompanha o estado e as coordenadas do jogador e verifica as ações usando ticks no lado do servidor, em vez de depender apenas de gatilhos do cliente.

-- coordenada fictícia
local VALID_JOB_COORD = vector3(125.0, 111.1, 35.83)
local MAX_ITEM_COUNT = 10

-- coordenada fictícia
local VALID_TURNIN_COORD = vector3(1888.0, 1254.1, 48.0)

local ITEM_NAME = 'log'

-- lista de jogadores com trabalhos ativos
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

-- processar tick do trabalho e incrementar quantidade de itens no lado do servidor
CreateThread(function()
    while true do
        for src, data in pairs(activeJobs) do
            local ped = GetPlayerPed(src)
            -- se não estiverem ao alcance, não queremos dar o item a eles
            if isPedWithinRange(ped, VALID_JOB_COORD) then
                -- dê o item a eles, mas limite a MAX_ITEM_COUNT
                data.itemCount = math.min(data.itemCount + 1, MAX_ITEM_COUNT)
            end
        end
        -- processar o tick do trabalho uma vez por segundo
        Wait(1000)
    end
end)

RegisterNetEvent("job:startJob", function()
    local ped = GetPlayerPed(source)
    -- se estiverem a menos de 15 unidades, estão fazendo o trabalho
    if isPedWithinRange(ped, VALID_JOB_COORD) then
        activeJobs[source] = {
            itemCount = 0,
        }
    end
end)

RegisterNetEvent("job:givePlayerItem", function()
    local ply = FX.GetPlayerFromSource(source)
    -- se não tiverem um trabalho ativo, não deveriam estar acionando isso!
    local jobData = activeJobs[source]
    if not jobData then return end

    local ped = GetPlayerPed(source)
    -- eles não estão dentro do alcance das coordenadas de entrega, rejeite as alterações
    if not isPedWithinRange(ped, VALID_TURNIN_COORD) then return end

    -- redefina os dados do item para que não possam acioná-lo várias vezes
    activeJobs[source] = nil

    -- adicionar itens ao usuário validados no lado do servidor
    ply.addItem(ITEM_NAME, jobData.itemCount)
end)

Opções do proprietário do servidor (Convars)

Observe que as seguintes configurações não devem ser modificadas a menos que você saiba exatamente o que está fazendo. A equipe do Cfx.re / Adhesive está sempre trabalhando duro para evitar trapaceiros. A maioria desses recursos estará ativada por padrão a partir da versão de compilação do FXServer 8450 e superiores.

  • sv_kick_players_cnl_timeout_sec: Este é o tempo limite no qual o servidor expulsará o jogador (por exemplo, se for 600, expulsa após 10 minutos sem conexão CnL).
  • sv_kick_players_cnl_update_rate_sec: Esta é a frequência com que a lista de jogadores é consultada no CnL.
  • sv_pure_verify_client_settings: Substitui a solicitação periódica ao info.json no cliente. Estabelece uma conexão segura entre adhesive e svadhesive e verifica algumas das sv_settings, como pureLevel, scripthook e outras configurações.
  • sv_kick_players_cnl_consecutive_failures: Quantas falhas seguidas são necessárias ver além do timeout_sec para expulsar um jogador. O padrão é definido como 2, indicando que se um jogador não se conectar por 10 minutos e depois perder a próxima atualização de conexão, ele será expulso. Isso serve como um mecanismo de segurança.
  • sv_authMaxVariance: A variância indica a probabilidade de o ID do usuário mudar para um determinado provedor (ou seja, 'steam', 'ip' ou 'license').
  • sv_authMinTrust: A confiança indica a probabilidade de a identidade do usuário ser forjada por um cliente malicioso.
  • sv_filterRequestControl: Uma variável de console usada para bloquear o roteamento de REQUEST_CONTROL_EVENT com base em uma política configurável.
  • sv_disableClientReplays: Ativar isso visa reduzir as chances de opções de trapaça. Observe que isso desativará o Rockstar Editor.

Resultados no jogador

Ter essas convars ativas provavelmente fará com que o jogador seja expulso com o seguinte motivo: Connection to CNL timed out.

Ser expulso não significa que o jogador seja automaticamente banido globalmente (Global Banned). No entanto, fornece uma forte indicação da confiabilidade do jogador, o que é extremamente útil para avaliar a confiabilidade.

Importante saber

Os códigos fornecidos não devem funcionar no método de copiar e colar. São apenas algumas dicas para evitar ações que possam acontecer no servidor. Isso requer conhecimento de programação. Você sempre pode entrar em nosso Discord para obter ajuda adicional.