20 mai 2026
5 min de lecture
ZeroTrust Team

Sécurisez vos événements : Guide de sécurité des événements Lua FiveM

Les cheats permettent aux clients malveillants de déclencher des événements dans n'importe quel contexte. Apprenez à sécuriser la communication client-serveur, à implémenter la validation côté serveur et à configurer les convars de sécurité de FXServer.

L'équipe anti-triche essaie toujours d'améliorer l'anti-triche, mais parfois des choses passent à travers.

Dans ce guide, nous allons essayer de vous aider à couvrir certaines pratiques courantes que vous pouvez adopter pour rendre votre serveur plus sûr en verrouillant correctement vos événements.

Comprendre les événements réseau dans FiveM

Les cheats peuvent permettre au client de déclencher des événements dans n'importe quel contexte.

Quand nous parlons de contexte, nous voulons dire qu'ils peuvent exécuter client->serveur (via TriggerServerEvent) ou ressource client->ressource client (via TriggerEvent).

Utilisation correcte des gestionnaires d'événements en Lua

Lorsque vous travaillez avec des événements en Lua, il est crucial de les enregistrer correctement selon qu'ils sont appelés par le client ou le serveur.

Une erreur courante consiste à enregistrer des événements serveur qui ne sont pas censés être appelés par le client, ou vice-versa, ce qui peut entraîner de graves vulnérabilités de sécurité.

AddEventHandler

Utilisez AddEventHandler lorsque l'événement est destiné à être déclenché dans le même contexte, soit client-client, soit serveur-serveur. Cela garantit que les événements ne sont pas mis en réseau et ne peuvent pas être appelés par le côté opposé.

AddEventHandler("eventName", function(eventParam1, eventParam2)
    -- Le code ici sera exécuté une fois que l'événement est déclenché dans le même contexte.
end)

RegisterNetEvent

Utilisez RegisterNetEvent lorsque l'événement doit être déclenché dans différents contextes, comme de client à serveur ou de serveur à client.

Sous le capot

NOTE : Cela ne bloque pas l'exécution depuis le même contexte. Sous le capot, RegisterNetEvent est un wrapper qui correspond simplement à ceci : RegisterNetEvent("eventName") AddEventHandler("eventName", function() ... end)
RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- Le code ici sera exécuté une fois que l'événement est déclenché à travers différents contextes.
end)

Cet exemple est pour le client, et comme tout ce qui se trouve sur le client, il n'est pas infaillible et peut être manipulé par des clients tricheurs.

Si vous souhaitez bloquer l'exécution depuis le même contexte (comme empêcher un événement réseau de serveur à serveur d'être déclenché par le client), vous devez enregistrer votre événement et vérifier la source de l'expéditeur :

RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- Le serveur enverra l'id réseau `65535` pour les événements provenant du serveur
    if source ~= 65535 then return end
end)

Ajout de vérifications et de validations

Même si vous construisez un anti-triche solide, l'ajout de vérifications sur les événements du serveur les rend nettement plus sécurisés. C'est une pratique fortement recommandée, bien qu'elle ne prévienne pas tout. Ci-dessous, nous partageons quelques bons conseils.

  • Argent du joueur : Validez le solde et les transactions côté serveur.
  • State bags du joueur : Vérifiez les données d'état dans les sessions actives.
  • Objets d'inventaire du joueur : Vérifiez les noms et les quantités d'objets.
  • Position du joueur : Vérifiez les plages et les distances.
  • Expérience et niveau du joueur : Assurez-vous que les statistiques sont calculées côté serveur.
  • Permissions et rôles du joueur : Validez l'ACL côté serveur.

Règle générale

Assurez-vous de récupérer toutes les valeurs à l'aide de méthodes côté serveur, sans permettre aux joueurs de fournir ou de modifier ces valeurs. Veuillez noter que les vérifications côté client peuvent également être une bonne pratique pour l'expérience utilisateur (UX), mais elles peuvent être facilement contournées.

Cela garantit l'intégrité et la sécurité de votre environnement de jeu.

Exemples de modèles de sécurité courants

Tous les exemples ci-dessous supposent l'utilisation d'un framework (tel que ESX, QB-Core, etc.).

Mauvaise sécurité (Ne faites jamais cela)

Ceci est destiné à vous montrer de mauvaises façons de gérer les événements, vous ne devriez jamais faire cela. Ajouter directement des objets à l'utilisateur à partir de sa propre saisie est toujours une mauvaise pratique : vous devez toujours valider les saisies des utilisateurs.

RegisterNetEvent("job:givePlayerItem", function(item, count)
    local ply = FX.GetPlayerFromSource(source)
    -- L'ajout direct d'objets à l'utilisateur à partir de sa propre saisie est dangereux !
    ply.addItem(item, count)
end)

Bonne sécurité (Recommandé)

Voici un modèle de sécurité robuste. Le serveur suit l'état et les coordonnées du joueur, et vérifie les actions à l'aide de ticks côté serveur au lieu de s'en remettre uniquement aux déclencheurs du client.

-- coordonnée fictive
local VALID_JOB_COORD = vector3(125.0, 111.1, 35.83)
local MAX_ITEM_COUNT = 10

-- coordonnée fictive
local VALID_TURNIN_COORD = vector3(1888.0, 1254.1, 48.0)

local ITEM_NAME = 'log'

-- liste des joueurs avec un travail actif
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

-- traiter le tick de travail et incrémenter les objets côté serveur
CreateThread(function()
    while true do
        for src, data in pairs(activeJobs) do
            local ped = GetPlayerPed(src)
            -- s'ils ne sont pas à portée, nous ne voulons pas leur donner l'objet
            if isPedWithinRange(ped, VALID_JOB_COORD) then
                -- donnez-leur l'objet, mais limitez-le à MAX_ITEM_COUNT
                data.itemCount = math.min(data.itemCount + 1, MAX_ITEM_COUNT)
            end
        end
        -- traiter le tick de travail une fois par seconde
        Wait(1000)
    end
end)

RegisterNetEvent("job:startJob", function()
    local ped = GetPlayerPed(source)
    -- s'ils sont à moins de 15 unités, ils font le travail
    if isPedWithinRange(ped, VALID_JOB_COORD) then
        activeJobs[source] = {
            itemCount = 0,
        }
    end
end)

RegisterNetEvent("job:givePlayerItem", function()
    local ply = FX.GetPlayerFromSource(source)
    -- s'ils n'ont pas de travail actif, ils ne devraient pas atteindre cet événement !
    local jobData = activeJobs[source]
    if not jobData then return end

    local ped = GetPlayerPed(source)
    -- ils ne sont pas à portée des coordonnées de rendu, rejeter leurs modifications
    if not isPedWithinRange(ped, VALID_TURNIN_COORD) then return end

    -- réinitialiser les données de travail pour qu'ils ne puissent pas le déclencher plusieurs fois
    activeJobs[source] = nil

    -- ajouter les objets à l'utilisateur validés côté serveur
    ply.addItem(ITEM_NAME, jobData.itemCount)
end)

Options pour les propriétaires de serveurs (Convars)

Veuillez noter que les paramètres suivants ne doivent pas être modifiés à moins que vous ne sachiez exactement ce que vous faites. L'équipe Cfx.re / Adhesive travaille très dur pour empêcher les tricheurs. La plupart de ces fonctionnalités seront activées par défaut avec la version de build FXServer 8450 et supérieure.

  • sv_kick_players_cnl_timeout_sec : C'est le délai après lequel le serveur exclura le joueur (par exemple, si c'est 600, il l'exclura après 10 minutes sans connexion CnL).
  • sv_kick_players_cnl_update_rate_sec : C'est la fréquence à laquelle la liste des joueurs est interrogée avec CnL.
  • sv_pure_verify_client_settings : Remplace la requête périodique à info.json chez le client. Établit une connexion sécurisée entre adhesive et svadhesive et vérifie certains paramètres sv_settings comme pureLevel, scripthook et d'autres configurations.
  • sv_kick_players_cnl_consecutive_failures : Nombre d'échecs consécutifs nécessaires au-delà de timeout_sec pour exclure un joueur. Par défaut, la valeur est fixée à 2, ce qui signifie que si un joueur ne parvient pas à se connecter pendant 10 minutes puis manque la mise à jour suivante, il sera exclu. Cela sert de mécanisme de sécurité.
  • sv_authMaxVariance : La variance indique la probabilité que l'ID de l'utilisateur change pour un fournisseur donné (c'est-à-dire 'steam', 'ip' ou 'license').
  • sv_authMinTrust : La confiance indique la faible probabilité que l'identité de l'utilisateur soit usurpée par un client malveillant.
  • sv_filterRequestControl : Une variable de console utilisée pour bloquer le routage de REQUEST_CONTROL_EVENT en fonction d'une politique configurable.
  • sv_disableClientReplays : Activer cette option vise à réduire les opportunités de triche. Veuillez noter que cela désactivera le Rockstar Editor.

Résultats sur le joueur

L'activation de ces convars entraînera probablement l'exclusion du joueur avec le motif suivant : Connection to CNL timed out.

Être exclu ne signifie pas que le joueur est automatiquement banni globalement (Global Banned). Cependant, cela fournit une indication solide de la fiabilité du joueur, ce qui est extrêmement utile pour évaluer sa confiance.

Important à savoir

Les codes fournis ne sont pas censés fonctionner par simple copier-coller. Ce sont juste quelques conseils pour empêcher certaines actions qui pourraient se produire sur le serveur. Cela nécessite des connaissances en programmation. Vous êtes toujours libre de rejoindre notre Discord pour obtenir une aide supplémentaire.