HALLOWEEN31 Save 31% off on your first purchase
The Raven JournalNovember 22, 2024 · 3 min

Detection · Server

Server-side event validation in FiveM: secure your handlers

Any client can call TriggerServerEvent with any arguments. A vulnerable shop handler, the fixed version, a five-point checklist, and where an anticheat fits.

Most economy exploits on a FiveM server come down to one mistake: a server event handler that trusted whatever the client sent it. Server-side event validation fixes that in your own code. A client-side anticheat helps, but it cannot fix a handler that pays out to anyone who asks.

Any connected client can call a registered net event through TriggerServerEvent, with any arguments it wants. Cfx.re's own guide, Secure Your Events, says cheats can trigger events in any context. The danger starts when a handler treats those arguments as if a normal client sent them.

The exploit, in two lines

This is the kind of handler that gets servers wiped:

server.lua (vulnerable)
RegisterNetEvent('shop:buy', function(itemId, price)
    local src = source
    -- Trusts the client to send the right price.
    exports.ox_inventory:AddItem(src, itemId, 1)
    Player(src).state.cash = Player(src).state.cash - price
end)

A cheater calls TriggerServerEvent('shop:buy', 'weapon_combatpdw', 0) and walks off with a free weapon. A negative price pays them for it instead. No aimbot is involved, only a one-line script that any Lua executor can run.

The fix is to validate on the server:

server.lua (validated)
RegisterNetEvent('shop:buy', function(itemId)
    local src = source
    -- The client only names the item. The price comes from our own catalog.
    local item = ITEM_CATALOG[itemId]
    if not item then return end          -- unknown item id
    if not isPlayerNearShop(src) then return end  -- not at a shop
    local cash = Player(src).state.cash or 0
    if cash < item.price then return end  -- can't afford
    exports.ox_inventory:AddItem(src, itemId, 1)
    Player(src).state.cash = cash - item.price
end)

The handler now reads the price from a catalog the server controls, checks that the player is near a shop, and confirms they can pay. Nothing the client sends is trusted on its own.

Why a client-side anticheat is not enough

A client-side anticheat catches aimbots, mod menus and Lua executors before they fire an event, and you should run one. It works by spotting the cheat, and no anticheat spots every cheat on the day it ships. A validated handler does not depend on spotting anything. It refuses a bad request from any client, and the next cheat release does not change that.

What "validate everything" means in practice

Run every server event handler through this checklist:

  • Identity: never take a player ID from the client. Use source.
  • Authority: read the state the player should have (job, location, inventory) from your own server data, never from the event arguments.
  • Bounds: check that any number the client sends sits within sane limits. A money amount of 999,999,999 is not a purchase, and neither is a negative one.
  • Rate: the same player calling shop:buy 200 times a second is a script. Throttle handlers that are cheap to spam.
  • Catalog lookups: when the client sends an ID, look it up in a server-side catalog and take everything else (price, weight, properties) from there.

How Raven handles event protection

An anticheat lowers the risk on handlers you have not hardened yet. Raven's Server Event Protections ships on and set to ban. It covers the events of every resource Raven is installed into, refuses events a player should not fire, and names the event and its resource on each hit. A client-side version ships off.

When one of your own scripts trips it, you whitelist that event, or the whole resource, in Protection Rules. Raven Mind suggests a fix for each hit, including trusting a whole resource when the same one keeps tripping it. It never adds an event whitelist without an operator. The Server Event Protections docs cover how to whitelist without opening a hole. Raven does not replace the handler checks above.

Keep reading03