DocumentationSecuritySafe Server Events

🛡️ Safe Server Events

Server events are the backbone of QBCore resources—and the most common attack surface for cheaters. This guide shows how to validate payloads, enforce permissions, and monitor usage so that callbacks and net events stay secure.

Threat model

  • Players triggering events with forged payloads
  • NUI/JS exploits that spam callbacks
  • Misconfigured admin commands that grant elevated privileges
  • Third-party resources calling your events with unexpected data

Principles for secure events

  1. Explicit registration – keep event names scoped to your resource
  2. Input validation – reject anything that does not match the expected schema
  3. Authorization – verify job, gang, license, or custom permission flags
  4. Rate limiting – prevent spam or brute force attacks
  5. Audit logging – capture who triggered sensitive actions and when
🚨

Never trust the client. All permission checks, database writes, and money transfers must be handled server-side.

Implementation example

server.lua
local QBCore = exports['qb-core']:GetCoreObject()
local allowedJobs = {
    police = 2, -- requires grade 2+
    ambulance = 3
}
 
local function hasPermission(player)
    if not player then return false end
    local job = player.PlayerData.job
    local requiredGrade = allowedJobs[job.name]
    return requiredGrade and job.grade.level >= requiredGrade
end
 
local function validatePayload(data)
    if type(data) ~= 'table' then return false, 'Invalid payload type' end
    if type(data.target) ~= 'string' or #data.target < 4 then
        return false, 'Target citizen ID missing'
    end
    if type(data.reason) ~= 'string' or #data.reason > 120 then
        return false, 'Reason must be under 120 characters'
    end
    return true
end
 
local function throttle(source)
    local key = ('event:escort:%s'):format(source)
    local now = os.time()
    local last = GlobalState[key] or 0
 
    if now - last < 3 then
        return false, 'Slow down'
    end
 
    GlobalState[key] = now
    return true
end
 
RegisterNetEvent('qb-escort:server:start', function(data)
    local src = source
    local player = QBCore.Functions.GetPlayer(src)
 
    local ok, err = throttle(src)
    if not ok then
        QBCore.Functions.Notify(src, err, 'error')
        return
    end
 
    if not hasPermission(player) then
        DropPlayer(src, 'Unauthorized event usage detected')
        return
    end
 
    local valid, message = validatePayload(data)
    if not valid then
        TriggerClientEvent('QBCore:Notify', src, message, 'error')
        return
    end
 
    exports['qb-log']:CreateLog('security', 'Escort Event', 'green', (
        '%s (%s) started escort on %s for %s'
    ):format(player.PlayerData.name, player.PlayerData.citizenid, data.target, data.reason))
 
    TriggerClientEvent('qb-escort:client:start', src, data.target)
end)

Schema validation helpers

local function expectNumber(value, min, max)
    value = tonumber(value)
    if not value then return false end
    if min and value < min then return false end
    if max and value > max then return false end
    return true, value
end
 
local function expectEnum(value, options)
    return options[value] == true
end
 
local VALID_TYPES = {
    license = true,
    impound = true,
    arrest = true
}
 
RegisterNetEvent('qb-police:server:applyAction', function(data)
    local src = source
    local player = QBCore.Functions.GetPlayer(src)
    if not hasPermission(player) then return end
 
    if not expectEnum(data.type, VALID_TYPES) then
        TriggerClientEvent('QBCore:Notify', src, 'Action type not allowed', 'error')
        return
    end
 
    local ok, amount = expectNumber(data.fee, 0, 5000)
    if not ok then
        TriggerClientEvent('QBCore:Notify', src, 'Fee outside allowed range', 'error')
        return
    end
 
    -- Continue with business logic
end)

Monitoring and response

  • Centralize logs – forward security logs to Discord or SIEM
  • Alert on abuse – trigger events when rate limits are hit repeatedly
  • Instrument analytics – track event usage counts per player/job
  • Educate staff – share safe patterns with community developers

Integration tips

  • Pair with Resmon Basics to detect malicious spam by measuring CPU spikes
  • Use callbacks (QBCore.Functions.CreateCallback) when you need a direct response
  • Reference the NUI form tutorial for an end-to-end example that validates inputs server-side

Checklist

  • Event names use a unique prefix
  • All parameters validated on the server
  • Permissions enforced through job/role checks
  • Rate limiting implemented for sensitive actions
  • Logging integrated with your moderation workflow