🛡️ 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
- Explicit registration – keep event names scoped to your resource
- Input validation – reject anything that does not match the expected schema
- Authorization – verify job, gang, license, or custom permission flags
- Rate limiting – prevent spam or brute force attacks
- 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