Explorer
KNOW-ANT-FIVEM-002

Client-side validation only - Toujours valider côté serveur

Domaine
backend
Type
anti-pattern
Priorité
P2

Parent : [[INDEX-FIVEM-BACKEND]]

Anti-Pattern : Client-Side Validation Only (FiveM)

Erreur à éviter

Faire confiance aux données envoyées par le client sans validation serveur. Les clients peuvent être modifiés (cheat menus, mods, injecteurs).

Exemple incorrect

-- ❌ DANGER - Validation uniquement côté client
-- client.lua
RegisterCommand('givemoney', function(source, args)
    local amount = tonumber(args[1])
    if amount > 0 and amount < 10000 then -- Validation client seule
        TriggerServerEvent('giveMoney', amount)
    end
end)

-- server.lua (FAIBLE)
RegisterNetEvent('giveMoney')
AddEventHandler('giveMoney', function(amount)
    local src = source
    -- PAS DE VÉRIFICATION !
    Player(src).addMoney(amount) -- Le cheater peut envoyer n'importe quel montant
end)

Exploitation possible

-- Cheat menu peut injecter :
TriggerServerEvent('giveMoney', 999999999) -- Bypass client validation
TriggerServerEvent('giveMoney', -1000000) -- Negative = dupe glitch
TriggerServerEvent('revivePlayer', targetId, true) -- Force revive anyone

Bonne pratique

-- ✅ CORRECT - Validation côté serveur OBLIGATOIRE
-- server.lua
RegisterNetEvent('wasabi_ambulance:giveMoney')
AddEventHandler('wasabi_ambulance:giveMoney', function(amount)
    local src = source
    
    -- 1. Validation du type
    amount = tonumber(amount)
    if not amount then
        print(('[^3SECURITY^7] Player %s tried to send invalid amount'):format(src))
        return
    end
    
    -- 2. Validation des limites
    if amount <= 0 or amount > 10000 then
        print(('[^3SECURITY^7] Player %s tried to give invalid amount: %s'):format(src, amount))
        -- Optional: ban/kick via anticheat
        return
    end
    
    -- 3. Validation contextuelle (a-t-il le droit?)
    if not IsPlayerAuthorized(src, 'give_money') then
        print(('[^1SECURITY^7] Player %s unauthorized action'):format(src))
        return
    end
    
    -- 4. Validation de l'état (peut-il vraiment?)
    if not IsPlayerNearBank(src) then
        exports['wasabi_ems']:Notify(src, 'error', 'Vous devez être à la banque')
        return
    end
    
    -- 5. Exécution
    Player(src).addMoney(amount)
    LogTransaction(src, amount, 'give')
end)

Pattern de validation robuste

-- Utilitaire de validation
function ValidatePlayerAction(playerId, action, data)
    -- Check 1: Player exists
    if not DoesPlayerExist(playerId) then return false, 'Player not found' end
    
    -- Check 2: Rate limiting
    if IsPlayerRateLimited(playerId, action) then 
        return false, 'Rate limited' 
    end
    
    -- Check 3: Authorization
    if not IsPlayerAuthorized(playerId, action) then
        TriggerEvent('wasabi_anticheat:suspicious', playerId, 'unauthorized_'..action)
        return false, 'Unauthorized'
    end
    
    -- Check 4: Data validation
    if not ValidateData(data, action) then
        return false, 'Invalid data'
    end
    
    return true
end

Règle d'or

Never trust the client. Validate everything on the server.

Checklist de validation serveur

  • Type checking (tonumber, tostring)
  • Range validation (min/max)
  • Authorization (permissions/jobs)
  • State validation (peut-il faire ça maintenant?)
  • Distance check (est-il proche?)
  • Rate limiting (anti-spam)
  • Logging (pour audit)

Références

  • CFX Security Guidelines
  • ESX/QB-Core security best practices
  • Pattern WASABI : Validation des actions médicales