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

lua
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/3
Test

Un 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.

Rellena los huecos

Completa el guard que comprueba que el jugador existe antes de tocar nada en el servidor (QBCore).

1RegisterNetEvent('tienda:server:comprar', function(item)
2 local src = source
3 local Player = QBCore.Functions.GetPlayer(src)
4 if not then return end
5 -- … lógica validada en el servidor …
6end)
Pista

Si GetPlayer no encuentra al jugador devuelve nil; corta ahí mismo con return.

Corrige el error

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:

1RegisterNetEvent('tienda:server:vender')
2AddEventHandler('tienda:server:vender', function(item, precio)
3 local Player = QBCore.Functions.GetPlayer(source)
4 Player.Functions.AddMoney('cash', precio)
5end)

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:

How was this lesson?