20 mei 2026
5 min leestijd
ZeroTrust Team

Beveilig uw evenementen: FiveM Lua Event-beveiligingshandleiding

Cheats stellen kwaadwillende clients in staat om evenementen in elke context te activeren. Leer hoe u client-servercommunicatie beveiligd, server-side validatie implementeert en FXServer-beveiligingsconvars configureert.

Het anti-cheat team probeert de anti-cheat altijd te verbeteren, maar soms glippen er dingen tussendoor.

In deze handleiding proberen we een aantal algemene praktijken te behandelen die je kunt toepassen om je server veiliger te maken door je evenementen goed te beveiligen.

Netwerkevenementen in FiveM begrijpen

Cheats kunnen de client in staat stellen om evenementen in elke context te activeren.

Wanneer we context zeggen, bedoelen we dat ze client->server (via TriggerServerEvent) of client-resource->client-resource (via TriggerEvent) kunnen uitvoeren.

Correct gebruik van event handlers in Lua

Wanneer u met evenementen in Lua werkt, is het cruciaal om ze correct te registreren op basis van het feit of ze door de client of de server worden aangeroepen.

Een veelgemaakte fout is het registreren van serverevenementen die niet door de client mogen worden aangeroepen, of omgekeerd, wat kan leiden tot ernstige beveiligingskwetsbaarheden.

AddEventHandler

Gebruik AddEventHandler wanneer het evenement binnen dezelfde context moet worden geactiveerd, ofwel client-client of server-server. Dit zorgt ervoor dat de evenementen niet genetwerkt zijn en niet door de tegenpartij kunnen worden aangeroepen.

AddEventHandler("eventName", function(eventParam1, eventParam2)
    -- Code hier wordt uitgevoerd zodra het evenement binnen dezelfde context wordt geactiveerd.
end)

RegisterNetEvent

Gebruik RegisterNetEvent wanneer het evenement in verschillende contexten moet worden geactiveerd, zoals van client naar server of van server naar client.

Onder de motorkap

OPMERKING: Dit blokkeert de uitvoering niet vanuit dezelfde context. Onder de motorkap is RegisterNetEvent een wrapper die simpelweg het volgende is: RegisterNetEvent("eventName") AddEventHandler("eventName", function() ... end)
RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- Code hier wordt uitgevoerd zodra het evenement in verschillende contexten wordt geactiveerd.
end)

Dit voorbeeld is voor de client, en zoals met alles op de client is het niet waterdicht en kan het worden gemanipuleerd door valsspelende clients.

Als u de uitvoering vanuit dezelfde context wilt blokkeren (zoals het voorkomen dat een server-to-server netwerkevenement door de client wordt geactiveerd), moet u uw evenement registreren en de afzenderbron controleren:

RegisterNetEvent("eventName", function(eventParam1, eventParam2)
    -- Server stuurt net-id `65535` voor evenementen vanaf de server
    if source ~= 65535 then return end
end)

Controles en verificatie toevoegen

Zelfs als u een sterke anti-cheat bouwt, maakt het toevoegen van controles op serverevenementen ze aanzienlijk veiliger. Dit is een ten zeerste aanbevolen praktijk, hoewel het niet alles voorkomt. Hieronder delen we enkele goede tips.

  • Spelersgeld: Valideer saldo en transacties aan de serverzijde.
  • Speler state bags: Verifieer statusgegevens in actieve sessies.
  • Spelerinventarisitems: Controleer itemnamen en -aantallen.
  • Spelerspositie: Verifieer bereik en afstanden.
  • Spelerservaring en -niveau: Zorg ervoor dat statistieken aan de serverzijde worden berekend.
  • Spelersrechten en -rollen: Valideer server-side ACL.

Vuistregel

Zorg ervoor dat u alle waarden ophaalt met behulp van server-side methoden, en sta spelers niet toe de waarden zelf op te geven of te wijzigen. Houd er rekening mee dat clientcontroles ook een goede praktijk kunnen zijn voor UX, maar eenvoudig kunnen worden omzeild.

Dit garandeert de integriteit en veiligheid van uw spelomgeving.

Voorbeelden van veelvoorkomende beveiligingspatronen

Alle onderstaande voorbeelden gaan uit van een bepaald framework (zoals ESX, QB-Core, enz.).

Slechte beveiliging (Doe dit nooit)

Dit is bedoeld om u slechte manieren te laten zien om evenementen te doen, dit moet u nooit doen. Direct items toevoegen aan de gebruiker vanuit hun eigen invoer is altijd een slechte praktijk: u moet gebruikersinvoer altijd valideren.

RegisterNetEvent("job:givePlayerItem", function(item, count)
    local ply = FX.GetPlayerFromSource(source)
    -- Direct items toevoegen aan de gebruiker vanuit hun eigen invoer is onveilig!
    ply.addItem(item, count)
end)

Goede beveiliging (Aanbevolen)

Hier is een robuust beveiligingspatroon. De server houdt de spelerstatus en -coördinaten bij en verifieert acties met behulp van server-side ticks in plaats van uitsluitend te vertrouwen op client-triggers.

-- willekeurige coördinaat
local VALID_JOB_COORD = vector3(125.0, 111.1, 35.83)
local MAX_ITEM_COUNT = 10

-- willekeurige coördinaat
local VALID_TURNIN_COORD = vector3(1888.0, 1254.1, 48.0)

local ITEM_NAME = 'log'

-- lijst van spelers met actieve banen
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

-- verwerk job-tick en verhoog het aantal items aan de serverzijde
CreateThread(function()
    while true do
        for src, data in pairs(activeJobs) do
            local ped = GetPlayerPed(src)
            -- als ze niet binnen bereik zijn, willen we ze het item niet geven
            if isPedWithinRange(ped, VALID_JOB_COORD) then
                -- geef ze het item, maar beperk het tot MAX_ITEM_COUNT
                data.itemCount = math.min(data.itemCount + 1, MAX_ITEM_COUNT)
            end
        end
        -- verwerk job-tick eenmaal per seconde
        Wait(1000)
    end
end)

RegisterNetEvent("job:startJob", function()
    local ped = GetPlayerPed(source)
    -- als ze binnen 15 eenheden zijn, doen ze de baan
    if isPedWithinRange(ped, VALID_JOB_COORD) then
        activeJobs[source] = {
            itemCount = 0,
        }
    end
end)

RegisterNetEvent("job:givePlayerItem", function()
    local ply = FX.GetPlayerFromSource(source)
    -- als ze geen actieve baan hebben, mogen ze dit niet aanroepen!
    local jobData = activeJobs[source]
    if not jobData then return end

    local ped = GetPlayerPed(source)
    -- ze zijn niet binnen het bereik van de inlevercoördinaten, wijs hun wijzigingen af
    if not isPedWithinRange(ped, VALID_TURNIN_COORD) then return end

    -- reset de itemgegevens zodat ze deze niet meerdere keren kunnen activeren
    activeJobs[source] = nil

    -- voeg items toe aan de gebruiker na serverseitige validatie
    ply.addItem(ITEM_NAME, jobData.itemCount)
end)

Servereigenaar-opties (Convars)

Houd er rekening mee dat de volgende instellingen niet mogen worden gewijzigd tenzij u precies weet wat u doet. Het Cfx.re / Adhesive-team werkt altijd heel hard om valsspelers te voorkomen. De meeste van deze functies zijn standaard ingeschakeld vanaf FXServer-buildversie 8450 en hoger.

  • sv_kick_players_cnl_timeout_sec: Dit is de time-out waarna de server de speler zal kicken (bijv. als dit 600 is, kick ze na 10 minuten geen CnL-verbinding).
  • sv_kick_players_cnl_update_rate_sec: Dit is de frequentie waarmee CnL wordt opgevraagd met de spelerslijst.
  • sv_pure_verify_client_settings: Vervangt het periodieke verzoek aan info.json in de client. Brengt een veilige verbinding tot stand tussen adhesive en svadhesive en verifieert een aantal van de sv_settings, zoals pureLevel, scripthook en andere configuraties.
  • sv_kick_players_cnl_consecutive_failures: Hoeveel mislukkingen op rij we moeten zien na de timeout_sec om een speler te kicken. De standaardwaarde is ingesteld op 2, wat aangeeft dat als een speler gedurende 10 minuten niet incheckt en vervolgens de volgende incheck-update mist, hij zal worden gekickt. Dit dient als een faalveilig mechanisme.
  • sv_authMaxVariance: Variantie is hoe waarschijnlijk het is dat de ID van de gebruiker verandert voor een bepaalde provider (d.w.z. 'steam', 'ip' of 'license').
  • sv_authMinTrust: Vertrouwen is hoe onwaarschijnlijk het is dat de identiteit van de gebruiker wordt vervalst door een kwaadwillende client.
  • sv_filterRequestControl: Een consolevariabele die wordt gebruikt om REQUEST_CONTROL_EVENT-routering te blokkeren op basis van een configureerbaar beleid.
  • sv_disableClientReplays: Het inschakelen hiervan is bedoeld om de kans op valsspelen te verkleinen. Houd er rekening mee dat dit Rockstar Editor uitschakelt.

Resultaten voor de speler

Als deze convars actief zijn, wordt de speler waarschijnlijk gekickt met de volgende reden: Connection to CNL timed out.

Gekickt worden betekent niet dat de speler automatisch een Global Ban heeft. Het geeft echter een sterke indicatie van de betrouwbaarheid van de speler, wat uiterst nuttig is voor het beoordelen van de betrouwbaarheid.

Belangrijk om te weten

De geleverde codes zijn niet bedoeld om te functioneren op basis van kopiëren en plakken. Dit zijn slechts enkele tips om bepaalde acties te voorkomen die op de server kunnen plaatsvinden. Dit vereist enige programmeerkennis. Je bent altijd vrij om lid te worden van onze Discord voor extra hulp.