Seguridad: escribe recursos sin agujeros · Lección 2/3 · 9 min

Blindaje server-side y anti-spam

Tus propios recursos también deben ser seguros: valida todo en el servidor y limita el spam de eventos.

No basta con no meter backdoors ajenos; tus scripts tienen que ser a prueba de tramposos. Dos pilares: validar SIEMPRE en el servidor, y limitar cuántas veces se puede disparar un evento.

Checklist de validación (recordatorio)

  • El cliente solo PIDE; el servidor DECIDE y EJECUTA (dinero, items, permisos).
  • Valida tipo, rango y propiedad de cada argumento que llega del cliente.
  • Comprueba distancia: ¿el jugador está cerca del cajero/tienda que dice usar?
  • Nunca mandes precios, recompensas ni IDs sensibles desde el cliente.

Anti-spam de eventos

lua
local ultimo = {} -- [src] = timestamp

RegisterNetEvent('tienda:server:comprar')
AddEventHandler('tienda:server:comprar', function(...)
  local src = source
  local ahora = GetGameTimer()
  if ultimo[src] and (ahora - ultimo[src]) < 500 then return end -- máx 1 cada 0.5s
  ultimo[src] = ahora
  -- … lógica validada …
end)

Rate-limit por jugador

Un tramposo puede disparar tu evento 1000 veces por segundo. El rate-limit por src corta esos abusos (duplicar items, farmear dinero) de raíz.

Practica lo aprendido

0/3
Test

En un sistema seguro, ¿quién DECIDE y EJECUTA el movimiento de dinero o items?

Pista

El cliente está en la máquina del jugador; un tramposo lo controla.

Rellena los huecos

Completa el anti-spam por jugador: máximo un disparo por segundo.

1local ultimo = {}
2RegisterNetEvent('mercado:server:vender')
3AddEventHandler('mercado:server:vender', function(itemId, cantidad)
4 local src = source
5 local ahora = GetGameTimer()
6 if ultimo[src] and (ahora - ultimo[src]) < then return end
7 ultimo[] = ahora
8 -- … lógica validada …
9end)
Pista

Un segundo en milisegundos; y se guarda el tiempo por jugador (source).

Corrige el error

El cliente manda el precio (precioCliente) y el servidor se fía. Blíndalo: ignora ese dato, usa Config.Coste y valida la cantidad con tonumber.

Este código tiene un fallo:

1RegisterNetEvent('tienda:server:comprar')
2AddEventHandler('tienda:server:comprar', function(precioCliente, cantidad)
3 local src = source
4 local xPlayer = ESX.GetPlayerFromId(src)
5 local total = precioCliente * cantidad
6 xPlayer.removeMoney(total)
7end)

Reescríbelo corregido:

Pista

total = Config.Coste * cantidad, con cantidad = tonumber(cantidad) y comprobada > 0.

Reto: prográmalo tú

Añade un anti-spam a un evento de 'vender' para que un jugador no pueda dispararlo más de una vez por segundo.

Escríbelo tú en tu editor (VS Code) y pruébalo en tu servidor. Aquí se aprende haciéndolo, no copiando.

Ver pista

Tabla ultimo[src] con GetGameTimer(); si la diferencia < 1000 ms, return.

Escribe aquí tu solución:

¿Qué tal esta lección?