Validazione Eventi

INFO

La validazione server-side è la base della sicurezza FiveM. Ogni evento RegisterNetEvent lato server va trattato come input ostile.

ObiettivoDettagli
Cosa imparerai• Controllare source in ogni evento
• Validare tipi e valori degli argomenti
• Rate limiting per eventi abusabili
• Prevenire trigger di eventi non autorizzati
Prerequisiti• Eventi client/server (modulo 08/02)
• Nozioni ACE (modulo 11/05)

1. source è Sacro

source in un evento server è l’ID del giocatore che l’ha triggerato — ma non sempre attendibile se il tuo codice non lo usa correttamente.

Regola base

RegisterNetEvent('myresource:doSomething')
AddEventHandler('myresource:doSomething', function(...)
    local playerId = source
    -- Verifica che il giocatore esista e sia connesso
    local xPlayer = ESX.GetPlayerFromId(playerId)
    if not xPlayer then return end
    -- ... valida
end)

2. Validare Tipi e Valori

Gli argomenti che arrivano dal client possono essere di qualsiasi tipo (anche tabelle costruite male). Usa type() e controlli espliciti.

RegisterNetEvent('shop:buyItem')
AddEventHandler('shop:buyItem', function(item, quantity)
    local playerId = source
    local xPlayer = ESX.GetPlayerFromId(playerId)
    if not xPlayer then return end
 
    -- VALIDAZIONE TIPI
    if type(item) ~= 'string' then return end
    quantity = tonumber(quantity) or 1
 
    -- VALIDAZIONE VALORI
    if quantity < 1 or quantity > 999 then return end
 
    -- VALIDAZIONE DATI (item deve esistere e avere prezzo)
    local shopItem = Config.ShopItems[item]
    if not shopItem then return end
 
    -- Calcolo PREZZO LATO SERVER
    local total = shopItem.price * quantity
    if xPlayer.getAccount('money').money >= total then
        xPlayer.removeAccountMoney('money', total)
        xPlayer.addInventoryItem(item, quantity)
    end
end)

3. Non Fidarti di Coordinate e Distanze

Il client può inviare coordinate false. Per azioni nel mondo (interazioni, crafting, raccolta), ricalcola la distanza lato server usando i nativi server-side.

-- client.lua
exports.ox_target:addBoxZone({
    coords = vector3(250.0, -150.0, 29.5),
    size = vec3(2.0, 2.0, 2.0),
    options = {
        {
            name = 'harvest',
            label = 'Raccogli',
            onSelect = function()
                TriggerServerEvent('myresource:harvest')
            end
        }
    }
})
-- server.lua
RegisterNetEvent('myresource:harvest')
AddEventHandler('myresource:harvest', function()
    local playerId = source
 
    -- RICALCOLA la distanza lato server (il client non può mentire qui)
    local coords = GetEntityCoords(GetPlayerPed(playerId))
    local dist = #(coords - vector3(250.0, -150.0, 29.5))
 
    if dist > 5.0 then
        -- Il giocatore ha triggerato l'evento da lontano → possibile exploit
        return
    end
 
    -- Procedi con la raccolta
end)

TIP

Alcuni nativi come GetEntityCoords funzionano anche lato server. Usali per verificare posizioni, ped, veicoli in mano al giocatore.


4. Rate Limiting

Gli eventi economici o che danno vantaggi vanno limitati. Un giocatore può inviare 1000 eventi al secondo con un bot.

-- server.lua
local lastUse = {}
 
RegisterNetEvent('mine:extract')
AddEventHandler('mine:extract', function()
    local playerId = source
    local now = os.time()
 
    -- Cooldown minimo di 3 secondi
    if lastUse[playerId] and now - lastUse[playerId] < 3 then
        return
    end
    lastUse[playerId] = now
 
    -- ... raccolta
end)

NOTE

Per limiti più robusti (rate per minuto, bucket per player) considera una libreria dedicata. La cosa importante: mai eseguire azioni costose senza un minimo di throttling.


5. Proteggere Eventi con ACE

Se un evento deve essere usato solo da admin o da un gruppo specifico, aggiungi il controllo ACE.

RegisterNetEvent('admin:teleportPlayer')
AddEventHandler('admin:teleportPlayer', function(targetId)
    local playerId = source
 
    if not IsPlayerAceAllowed(playerId, 'command.tp') then
        return
    end
    -- ...
end)
add_ace group.admin command.tp allow

6. Non Fidarti degli Eventi Client di “Vendita”

Quando un giocatore “vende” qualcosa, il server deve verificare che l’item esista e scalarlo prima di dare soldi.

-- server.lua
RegisterNetEvent('dealer:sell')
AddEventHandler('dealer:sell', function(item, quantity)
    local playerId = source
    local xPlayer = ESX.GetPlayerFromId(playerId)
    if not xPlayer then return end
 
    quantity = tonumber(quantity) or 1
    if quantity < 1 or quantity > 100 then return end
 
    local itemData = Config.SellPrices[item]
    if not itemData then return end
 
    -- Verifica che abbia davvero l'item
    local hasItem = xPlayer.getInventoryItem(item).count
    if hasItem < quantity then return end
 
    -- PRIMA scala l'item, POI dai soldi
    xPlayer.removeInventoryItem(item, quantity)
    xPlayer.addMoney(itemData.price * quantity)
end)

Verifica

  • Ogni evento con effetti economici ricalcola prezzi lato server
  • source sempre verificato (giocatore esiste)
  • Tipi e limiti validati
  • Coordinate ricalcolate server-side dove conta
  • Cooldown su azioni ripetibili
  • Eventi admin protetti con ACE

TIP


Torna alla: Indice Generale