Cómo proteger los eventos de servidor de RedM: cada TriggerServerEvent es una entrada del atacante
Cómo proteger los eventos de servidor de RedM: por qué cada TriggerServerEvent es entrada del atacante, reglas de validación y qué añade un anticheat.
Si quieres proteger los eventos de servidor de RedM, parte de un hecho: el jugador controla el cliente, así que controla cada argumento de cada TriggerServerEvent. El dinero y los objetos que desaparecen de los servidores de rol rara vez salen por un ingenioso hack de memoria. Salen por un manejador que se fió de un número que le dieron.
Esta guía es para desarrolladores de recursos y dueños de servidores que revisan scripts. Los ejemplos son Lua neutro respecto al framework. Las mismas reglas valen para cualquier recurso de VORP, RSG o propio, y son lo que hace que un exploit de triggers típico en RedM falle en vez de pagar.
Por qué un evento lanzado por el cliente es una entrada controlada por el atacante
Un evento de servidor es una puerta pública. Tu script de cliente llama a TriggerServerEvent("shop:sell", item, count, price), y el servidor ejecuta el manejador. Nada en esa llamada demuestra que la petición venga de tu script. Un mod menu o un recurso inyectado puede llamar al mismo evento, con el mismo nombre, y poner lo que quiera en los argumentos.
Ocultar el nombre del evento no ayuda. El Lua del lado del cliente llega a todos los jugadores, y los nombres de evento aparecen en él. Trata el nombre como público y los argumentos como hostiles, como hace un desarrollador web con un campo de formulario.
Esa es toda la clase de exploits de VORP en una frase: un manejador que aplica lo que dice el cliente. El resto de esta guía trata de cómo dejar de hacerlo.
Un manejador vulnerable y el corregido
Esta es la forma que vemos con más frecuencia. El cliente envía el objeto, la cantidad y el precio, y el servidor paga.
RegisterNetEvent('shop:sell', function(item, count, price)
local src = source
-- takes the client's word for everything
removeItem(src, item, count)
addMoney(src, count * price)
end)Cada línea es un error. Quien llama elige el precio, así que una sola llamada con un price enorme fabrica dinero. Un count negativo convierte la retirada en un regalo. Puede que nunca se compruebe que el jugador tiene el objeto. Y el manejador tampoco pregunta si el jugador está en una tienda.
La versión corregida mantiene los precios en el servidor, valida tipos y rangos, contrasta al jugador con el mundo y rechaza en silencio cuando algo no cuadra. removeItem, addMoney y getItemCount representan las funciones propias de tu framework.
local PRICES = { apple = 2, bread = 3 } -- authority lives here
local SHOP = vector3(-324.0, 803.0, 117.0) -- example coordinates
local lastCall = {}
local function refuse(src, why)
print(('[shop:sell] refused src=%s reason=%s'):format(src, why))
end
RegisterNetEvent('shop:sell', function(item, count)
local src = source -- never trust an id from the client
local now = GetGameTimer()
if lastCall[src] and now - lastCall[src] < 1000 then
return refuse(src, 'rate')
end
lastCall[src] = now
if type(item) ~= 'string' or type(count) ~= 'number'
or count % 1 ~= 0 or count < 1 or count > 50 then
return refuse(src, 'bad_args')
end
local price = PRICES[item]
if not price then return refuse(src, 'unknown_item') end
local ped = GetPlayerPed(src)
if #(GetEntityCoords(ped) - SHOP) > 5.0 then
return refuse(src, 'too_far')
end
if getItemCount(src, item) < count then
return refuse(src, 'not_owned')
end
removeItem(src, item, count)
addMoney(src, count * price) -- the server computes the reward
end)
AddEventHandler('playerDropped', function() lastCall[source] = nil end)Ahora el cliente envía dos valores, y el servidor trata ambos como sugerencias. Cada regla de ese manejador merece su propio párrafo.
Las reglas para proteger los eventos de servidor de RedM
1. Identifica al jugador con source, y solo con source
Dentro de un manejador de evento de servidor, source es el jugador que lo lanzó, y lo fija la plataforma, no el cliente. Captúralo en una variable local al principio (local src = source) porque puede cambiar tras un yield. Nunca aceptes un argumento «id de jugador» o «id de objetivo» para decidir quién cobra o quién sale herido.
2. Nunca aceptes como verdad cantidades, precios, nombres de objeto u objetivos
Si el cliente tiene que decir lo que quiere, búscalo tú. Los precios, las recompensas y las listas de objetos pertenecen a una tabla del lado del servidor. Si un argumento es un objetivo (otro jugador, una entidad, una coordenada), comprueba que existe y que quien llama tiene permiso para actuar sobre él. Los eventos más seguros casi no tienen argumentos.
3. Valida tipos y rangos
Lua pasará encantado una tabla donde esperabas una cadena, o -1, o un decimal, o un número tan grande que la aritmética se comporta mal. Comprueba type(), exige enteros donde quieres enteros y fija un límite superior estricto que corresponda a lo que la función puede hacer de forma legítima. Rechaza, no recortes: una petición recortada sigue sin decirte nada de quién la envió.
4. Comprueba distancia, trabajo y estado en el servidor
Una venta legítima ocurre en una tienda, un crafteo legítimo en un banco de trabajo, un arresto legítimo lo hace un agente de la ley. Verifícalo con datos del servidor: la posición del jugador (con OneSync, las natives de servidor como GetPlayerPed y GetEntityCoords funcionan), su trabajo según tu framework, su estado actual según tus propias tablas. Que la interfaz del cliente oculte un botón no es una comprobación.
5. Deja que el servidor calcule la recompensa
El patrón más fuerte es también el más simple: el cliente dice «he terminado», el servidor decide cuánto valía. Para actividades más largas, inicia la actividad en el servidor y anota cuándo. Cuando el cliente informa de que terminó, comprueba que pasó el tiempo suficiente y que existe una sesión, y luego paga una cantidad fijada en el servidor.
6. Limita la tasa de todo evento que mueva valor
Un manejador correcto una vez aún puede llamarse doscientas veces por segundo. Guarda una marca de tiempo o un contador por jugador, y descarta lo que supere lo que un humano podría hacer. Limpia la tabla en playerDropped, como en el ejemplo. Los reenvíos y las inundaciones de peticiones también cargan al servidor, así que un retorno temprano barato importa.
7. No expongas eventos de admin a los clientes
Un evento llamado admin:giveMoney que no comprueba nada es un regalo. Si las herramientas del staff tienen que usar eventos, verifica en el servidor a quien llama cada vez, con el sistema de permisos del framework o con ACE, no con una bandera del lado del cliente. Mejor aún, usa comandos registrados en el servidor, o eventos que solo se lancen de servidor a servidor.
8. Registra los rechazos
La función refuse de arriba no es decoración. Un rechazo es una señal: o un error en tu propio script de cliente, o alguien que está tanteando. Una línea por rechazo, con el jugador y el motivo, convierte tus registros en una lista de pistas. Una advertencia: un rechazo por sí solo no prueba nada, así que revisa antes de banear.
Dónde falla esto en la práctica
Scripts heredados. Un recurso de pago o filtrado que se fía de sus argumentos sigue siendo vulnerable hasta que alguien lo lee. Busca
RegisterNetEventyRegisterServerEventen los recursos de tu servidor y lee cada manejador que toque dinero, objetos, trabajos o inventario.Eventos de depuración «temporales». El evento añadido para probar y nunca retirado.
Fiarse de una bandera fijada por el cliente.
isAdmin,isOnDutyy valores parecidos enviados como argumentos son solo más entrada del atacante.Arreglar un solo manejador. Si tres recursos pagan desde la misma función de inventario, el llamador más débil decide tu seguridad.
Cuando el daño ya está hecho, el patrón en los datos se parece a un dupe o a un valor atípico de patrimonio repentino. Nuestro análisis del exploit de dupe de dinero en RedM explica cómo detectarlo desde el lado de la economía.
Lo que añade un anticheat a la protección de eventos en RedM
Los manejadores cuidadosos eliminan la vulnerabilidad. Un anticheat vigila a quienes lo intentan igualmente, y a los manejadores que aún no has revisado. Baobab AC lo hace del lado del servidor, y su catálogo tiene cuatro módulos que importan aquí. Mira todas las funciones para la lista completa.
Anti Trigger en Lista Negra impide que los menús de trampas llamen a eventos de servidor ocultos para hacer cosas como cambios de trabajo no autorizados o eludir las reglas de la economía.
Anti Spam de Triggers bloquea la inundación de solicitudes destinada a saturar el backend del servidor.
Anti Exploit de Trigger de Economía impide que los tramposos inyecten comandos ocultos falsos para darse dinero, oro u objetos valiosos.
Anti inyección nativa engancha las natives que crean entidades y cambian el estado del jugador, de modo que un spawn o un cambio de estado que ningún recurso del servidor pidió se detecta en el momento en que ocurre.
En los planes Pro y Network, el Análisis económico añade una auditoría de solo lectura de la base de datos de tu juego, cruzada con el tiempo de juego que solo conoce el anticheat: masa monetaria a lo largo del tiempo, valores atípicos de patrimonio, dupes de objetos, stacks imposibles. Cada resultado es una pista para que la revise una persona, nunca una sanción automática. La detección es agnóstica del framework, y VORP Core es la referencia para los módulos de economía. La guía del anticheat VORP entra en esa configuración, y cómo se pilla a un mod menu explica el lado de la detección de la misma historia.
Puedes ver qué aspecto tiene esa salida en el panel de demostración, y comparar planes en la página de precios.
Lo que un anticheat no puede hacer
Vale la pena enunciar otros tres límites. La detección es probabilística, así que la postura entregada es observar: una detección dudosa cuesta una línea en una lista, no una expulsión. El Análisis económico te dice que algo parece raro, no por qué, y una persona tiene que leerlo. Y un anticheat ve el cliente y el tráfico, no tu lógica de negocio, así que solo tú sabes que una venta de 400 dólares en esa tienda es imposible.
Las dos capas encajan. La autoridad del lado del servidor hace imposible el exploit. El anticheat atrapa el intento, y la auditoría muestra lo que se coló antes de que lo arreglaras. Si quieres el argumento más amplio de por qué la confianza tiene que estar en el servidor, lee anticheat del lado del servidor frente al del cliente.
Una lista para proteger hoy los eventos de servidor de RedM
- 1
Lista cada
RegisterNetEventyRegisterServerEventde tus recursos. - 2
Para cada uno que toque dinero, objetos, trabajos o inventario, lee el manejador.
- 3
Sustituye cada precio, recompensa e id de jugador que aporte el cliente por una consulta en el servidor o por
source. - 4
Añade comprobaciones de tipo y de rango, y luego una de posición o de estado.
- 5
Añade un límite de tasa por jugador y un registro de rechazos.
- 6
Retira los eventos de depuración y de admin que se saltan las comprobaciones.
- 7
Solo entonces activa módulos de anticheat para cubrir lo que la revisión no vio. Empieza en observar, lee las alertas y aprieta desde ahí. Cómo funciona un anticheat RedM explica el flujo.
Un manejador confiado es barato de escribir y caro de encontrar. Una hora leyendo tus eventos de servidor ahora cuesta menos que una noche con la economía robada.