Validazione Eventi
INFO
La validazione server-side è la base della sicurezza FiveM. Ogni evento
RegisterNetEventlato server va trattato come input ostile.
| Obiettivo | Dettagli |
|---|---|
| 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
GetEntityCoordsfunzionano 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 allow6. 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
-
sourcesempre verificato (giocatore esiste) - Tipi e limiti validati
- Coordinate ricalcolate server-side dove conta
- Cooldown su azioni ripetibili
- Eventi admin protetti con ACE
TIP
Prossimo passo: 03 - Proteggere Economia e Item
Torna alla: Indice Generale