Debugging and securing your resources · Lesson 3/3 · 9 min
Server-side hardening, anti-spam and checklist
Your own resources must also be secure: validate everything on the server and limit event spam.
It's not enough to avoid other people's backdoors; your scripts have to be cheat-proof. Two pillars: ALWAYS validate on the server, and limit how many times an event can be fired.
Event anti-spam
local ultimo = {} -- [src] = timestamp
RegisterNetEvent('tienda:server:comprar', function(...)
local src = source
local ahora = GetGameTimer()
if ultimo[src] and (ahora - ultimo[src]) < 500 then return end -- max 1 every 0.5s
ultimo[src] = ahora
-- ... logic validated with QBCore.Functions.GetPlayer ...
end)Per-player rate-limit
Checklist before publishing yours
- All sensitive logic (money, items, permissions) is decided and executed on the server.
- Validate the type, range and ownership of every client argument.
- Parameterized queries (?), no concatenating SQL.
- No hardcoded secrets: they go in server.cfg / convars via GetConvar.
- Anti-spam on the events that give money/items.
- config.lua for what's configurable; no stray magic values.
Before INSTALLING something from outside: run it through the Security Audit, look for os.execute / load( / webhooks, distrust anything heavily obfuscated and test it first on a test server.
Practice what you learned
0/3Un jugador manda al servidor el evento 'tienda:server:vender' con item y precio. ¿Por qué NO debes usar el precio que llega?
Pista
¿Quién controla lo que se envía desde el cliente? El jugador. Por eso no te fías.
Completa el guard que comprueba que el jugador existe antes de tocar nada en el servidor (QBCore).
RegisterNetEvent('tienda:server:comprar', function(item) local src = source local Player = QBCore.Functions.GetPlayer(src) if not then return end -- … lógica validada en el servidor …end)Pista
Si GetPlayer no encuentra al jugador devuelve nil; corta ahí mismo con return.
Este evento confía en el precio que manda el cliente y no comprueba al jugador. Arréglalo: añade el guard del Player y lee el precio de Config en el servidor, no del cliente.
Este código tiene un fallo:
RegisterNetEvent('tienda:server:vender')AddEventHandler('tienda:server:vender', function(item, precio) local Player = QBCore.Functions.GetPlayer(source) Player.Functions.AddMoney('cash', precio)end)Reescríbelo corregido:
Pista
Quita 'precio' de los argumentos del handler, añade 'if not Player then return end' y léelo con local precio = Config.Precios[item] dentro del servidor.
Challenge: code it yourself
Add anti-spam to an event 'mercado:server:vender' so a player can't fire it more than once per second.
Write it yourself in your editor (VS Code) and test it on your server. You learn here by doing it, not by copying.
See hint
Table ultimo[src] with GetGameTimer(); if the difference < 1000 ms, return.
Escribe aquí tu solución:
