Los cheats permiten a los clientes maliciosos activar eventos en cualquier contexto. Aprende a proteger la comunicación cliente-servidor, implementar validación del lado del servidor y configurar convars de seguridad de FXServer.
El equipo anti-cheat siempre está intentando mejorar el sistema, pero a veces las cosas se filtran.
En esta guía intentaremos ayudarte a cubrir algunas prácticas comunes que puedes realizar para hacer que tu servidor sea más seguro bloqueando adecuadamente tus eventos.
Los cheats pueden permitir al cliente activar eventos en cualquier contexto.
Cuando decimos contexto nos referimos a que pueden ejecutar cliente->servidor (a través de TriggerServerEvent) o recurso del cliente->recurso del cliente (a través de TriggerEvent).
Al trabajar con eventos en Lua, es crucial registrarlos correctamente según si son llamados por el cliente o por el servidor.
Un error común es registrar eventos del servidor que no deben ser llamados por el cliente, o viceversa, lo que puede provocar graves vulnerabilidades de seguridad.
Utiliza AddEventHandler cuando el evento esté deba activarse dentro del mismo contexto, ya sea cliente-cliente o servidor-servidor. Esto asegura que los eventos no se transmitan por red y no puedan ser llamados por el lado opuesto.
AddEventHandler("eventName", function(eventParam1, eventParam2)
-- El código aquí se ejecutará una vez que el evento se active dentro del mismo contexto.
end)Utiliza RegisterNetEvent cuando el evento deba activarse en diferentes contextos, como del cliente al servidor o del servidor al cliente.
RegisterNetEvent("eventName", function(eventParam1, eventParam2)
-- El código aquí se ejecutará una vez que el evento se active a través de diferentes contextos.
end)Este ejemplo es para el cliente y, como todo en el cliente, no es infalible y puede ser manipulado por clientes con trampas.
Si deseas bloquear la ejecución desde el mismo contexto (como evitar que un evento de red de servidor a servidor sea activado por el cliente), debes registrar tu evento y verificar la fuente del remitente:
RegisterNetEvent("eventName", function(eventParam1, eventParam2)
-- El servidor enviará el ID de red `65535` para eventos del servidor
if source ~= 65535 then return end
end)Incluso si construyes un sistema anti-cheat sólido, añadir comprobaciones en los eventos del servidor los hace significativamente más seguros. Esta es una práctica muy recomendada, aunque no lo previene todo. A continuación, compartimos algunos buenos consejos.
Esto asegura la integridad y seguridad de tu entorno de juego.
Todos los ejemplos a continuación asumen algún tipo de framework (como ESX, QB-Core, etc.).
Esto está pensado para mostrarte formas incorrectas de hacer eventos, nunca deberías hacer esto. Añadir objetos directamente al usuario a partir de su propia entrada es siempre una mala práctica: siempre debes validar la entrada del usuario.
RegisterNetEvent("job:givePlayerItem", function(item, count)
local ply = FX.GetPlayerFromSource(source)
-- ¡Añadir objetos directamente al usuario desde su propia entrada es peligroso!
ply.addItem(item, count)
end)Aquí tienes un patrón de seguridad robusto. El servidor realiza un seguimiento del estado y las coordenadas del jugador, y verifica las acciones mediante ticks en el servidor en lugar de depender únicamente de los activadores del cliente.
-- coordenada ficticia
local VALID_JOB_COORD = vector3(125.0, 111.1, 35.83)
local MAX_ITEM_COUNT = 10
// coordenada ficticia
local VALID_TURNIN_COORD = vector3(1888.0, 1254.1, 48.0)
local ITEM_NAME = 'log'
-- lista de jugadores con trabajos activos
local activeJobs = {}
AddEventHandler("playerDropped", function ()
if not activeJobs[source] then return end
activeJobs[source] = nil
end)
function isPedWithinRange(ped, tgtCoords)
return #(GetEntityCoords(ped) - tgtCoords) < 15.0
end
-- procesar el tick de trabajo e incrementar la cantidad de objetos del lado del servidor
CreateThread(function()
while true do
for src, data in pairs(activeJobs) do
local ped = GetPlayerPed(src)
-- si no están dentro del rango, no queremos darles el objeto
if isPedWithinRange(ped, VALID_JOB_COORD) then
-- darles el objeto, pero limitarlo a MAX_ITEM_COUNT
data.itemCount = math.min(data.itemCount + 1, MAX_ITEM_COUNT)
end
end
-- procesar el tick de trabajo una vez por segundo
Wait(1000)
end
end)
RegisterNetEvent("job:startJob", function()
local ped = GetPlayerPed(source)
-- si están dentro de 15 unidades, están haciendo el trabajo
if isPedWithinRange(ped, VALID_JOB_COORD) then
activeJobs[source] = {
itemCount = 0,
}
end
end)
RegisterNetEvent("job:givePlayerItem", function()
local ply = FX.GetPlayerFromSource(source)
-- si no tienen un trabajo activo, ¡no deberían llegar a esto!
local jobData = activeJobs[source]
if not jobData then return end
local ped = GetPlayerPed(source)
-- no están dentro del rango de las coordenadas de entrega, rechaza sus cambios
if not isPedWithinRange(ped, VALID_TURNIN_COORD) then return end
-- restablecer los datos del objeto para que no puedan activarlo varias veces
activeJobs[source] = nil
-- añadir objetos al usuario validados en el servidor
ply.addItem(ITEM_NAME, jobData.itemCount)
end)Ten en cuenta que las siguientes configuraciones no deben modificarse a menos que sepas exactamente lo que estás haciendo. El equipo de Cfx.re / Adhesive siempre está trabajando arduamente para evitar trampas. La mayoría de estas características estarán habilitadas de forma predeterminada con la versión de compilación de FXServer 8450 y superior.
Tener estas convars activas probablemente hará que el jugador sea expulsado con la siguiente razón: Connection to CNL timed out.
Ser expulsado no significa que el jugador sea automáticamente baneado globalmente (Global Banned). Sin embargo, proporciona una fuerte indicación de la fiabilidad del jugador, lo cual es extremadamente útil para evaluar su nivel de confianza.
Los códigos proporcionados no están pensados para funcionar mediante copiar y pegar. Son solo algunos consejos para evitar acciones que podrían ocurrir en el servidor. Esto requiere conocimientos de programación. Siempre eres libre de unirte a nuestro Discord para obtener ayuda adicional.