Roblox usa un sistema de física distribuida en el que los clientes tienen custodia sobre la simulación física de objetos en su control, generalmente el personaje del jugador y los objetos no anclados cerca de ese personaje. Además, a través del uso de código de terceros, los explotadores pueden ejecutar cualquier código Lua en el cliente para manipular el modelo de datos del cliente y descompilar y ver el código que se ejecuta en él.
Colectivamente, esto significa que un explotador cualificado puede potencialmente ejecutar código para engañar en tu juego, incluido:
- Teletransportar a su propio personaje alrededor del lugar.
- Ejecutando un RemoteEvents o invocando un RemoteFunctions, como otorgarse artículos sin ganarlos.
- Ajustar la velocidad de su personaje a WalkSpeed para que se mueva muy rápido.
Mientras puede implementar limitadas defensas de diseño para atrapar ataques comunes, se recomienda altamente que implemente más tácticas de mitigación de lado del servidor, ya que el servidor es la autoridad máxima para cualquier experiencia en ejecución.
Tácticas de diseño defensivo
Las decisiones básicas de diseño pueden servir como medidas de seguridad "de primer paso" para desalentar los exploits. Por ejemplo, en un juego de tiro en el que los jugadores obtienen puntos por matar a otros jugadores, un hacker puede crear un montón de bots que se teletransportan al mismo lugar para que se puedan matar rápidamente por puntos. Dado este potencial código malicioso, vulnerabilidad del sistema, considere dos enfoques y su resultado predictable:
Acercarse | Resultado predictible |
---|---|
Persigue a los bots escribiendo código que intenta detectarlos. | |
Reducir o eliminar completamente las ganancias de puntos por muerte en los jugadores recién generados. |
Aunque el diseño defensivo obviamente no es una solución perfecta o completa, puede contribuir a un enfoque de seguridad más amplio, junto con medidas de mitigación del lado del servidor .
Mitigación del lado del servidor
En la medida de lo posible, el servidor debería emitir el veredicto final sobre lo que es "verdadero" y lo que es el estado actual del mundo. Los clientes pueden, por supuesto, solicitar al servidor que realice cambios o realice una acción, pero el servidor debería valorar y aprobar cada uno de estos cambios / acciones antes de que los resultados se repliquen a otros jugadores.
Con la excepción de algunas operaciones de física, los cambios en el modelo de datos en el cliente no se replican al servidor, por lo que el camino de ataque principal a menudo es a través de los eventos de red que has declarado con RemoteEvents y RemoteFunctions . Recuerda que un atacante que ejecuta su propio código en tu cliente puede invocar estos con cualquier dato que quieras
Validación del tipo de tiempo de ejecución remoto
Una ruta de ataque es para que un explotador invoque RemoteEvents y RemoteFunctions con argumentos del introducirincorrecto. En algunos escenarios, esto puede causar que el código en el servidor que escucha estos controles remotos de forma desfavorable para el explotador.
Al utilizar eventos/funciones remotos, puede evitar este tipo de ataque al validar los tipos de argumentos pasados en el servidor. El módulo “t” , disponible aquí, es útil para verificar el tipo de chequeo en este modo. Por ejemplo, suponiendo que el código del módulo exista como un 1> Class.
LocalScript en StarterPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")-- Pasar el color y la posición de la parte al invocar la funciónlocal newPart = remoteFunction:InvokeServer(Color3.fromRGB(200, 0, 50), Vector3.new(0, 25, 0))if newPart thenprint("The server created the requested part:", newPart)elseif newPart == false thenprint("The server denied the request. No part was created.")end
Script en ServerScriptService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
local t = require(ReplicatedStorage:WaitForChild("t"))
-- Cree un validador de tipo en el avance para evitar el sobrepeso innecesario
local createPartTypeValidator = t.tuple(t.instanceIsA("Player"), t.Color3, t.Vector3)
-- Crea una nueva parte con las propiedades pasadas
local function createPart(player, partColor, partPosition)
-- Compruebe si los argumentos pasados son válidos
if not createPartTypeValidator(player, partColor, partPosition) then
-- Regrese silenciosamente "false" si el tipo de comprobación falla aquí
-- Subir un error sin un tiempo de reutilización puede ser abusado para bloquear el servidor
-- ¡Proporciona comentarios del cliente en su lugar!
return false
end
print(player.Name .. " requested a new part")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = workspace
return newPart
end
-- Vincular "crear parte()" a la función de llamada devolución de llamada
remoteFunction.OnServerInvoke = createPart
Validación de datos
Otro ataque que los explotadores podrían lanzar es enviar tipos técnicamente válidos pero hacerlos extremadamente grandes, largos o de otra manera malformados. Por ejemplo, si el servidor tiene que realizar una operación costosa en una cadena que se escala con longitud, un atacante podría enviar una cadena extremadamente grande o malformada para obstruir el servidor.
Del mismo modo, tanto inf y NaN``Global.LuaGlobals.type() como 1> number1> , pero ambos pueden causar problemas mayores si un explotador los envía y no se manejan correctamente a través de funciones como las siguiendo:
local function isNaN(n: number): boolean
-- NaN nunca es igual a sí mismo
return n ~= n
end
local function isInf(n: number): boolean
-- El número podría ser -inf o inf
return math.abs(n) == math.huge
end
Otro ataque común que los explotadores pueden usar implica enviar tables en lugar de un Instance . Los cargamentos complejos pueden imitar lo que sería una referencia de objeto ordinaria de otra manera.
Por ejemplo, proporcionado con un sistema de tienda en la experiencia en el que los datos de los artículos, como los precios, se almacenan en NumberValue objetos, un hacker puede evitar todos los demás controles haciendo lo siguiendo:
LocalScript en StarterPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")local payload = {Name = "Ultra Blade",ClassName = "Folder",Parent = itemDataFolder,Price = {Name = "Price",ClassName = "NumberValue",Value = 0, -- ¡También se podrían usar valores negativos, lo que daría la moneda en lugar de tomarla!},}-- Enviar carga útil maliciosa al servidor (se rechazará)print(buyItemEvent:InvokeServer(payload)) -- Muestra "fallo Imposible proporcionar"-- Envía un artículo real al servidor (¡esto pasará!)print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Outputs "true" and remaining currency if purchase succeeds
Script en ServerScriptService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- Compruebe si el artículo pasado no está spoofed y está en la carpeta ItemData
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Invalid item provided"
end
-- El servidor puede entonces continuar para procesar la compra en función del flujo de ejemplo a continuación
end
-- Vincular "buyItem()" a la llamada de función remota
buyItemEvent.OnServerInvoke = buyItem
Validación de valores
Además de validar tipos y datos, deberías validar los valores pasados a través de 1> Class.RemoteEvent|RemoteEvents1> y 4> Class.RemoteFunction|RemoteFunctions4>, asegurándose de que son válidos y lógicos en el contexto solicitado. Dos ejemplos comunes
Tienda de Experiencia
Considera un sistema de tienda de experiencia con un menú de usuario, por ejemplo, un menú de selección de productos con un botón "Comprar". Cuando se presiona el botón, puedes invocar un RemoteFunction entre el cliente y el servidor para solicitar la compra. Sin embargo, es importante que el serVID, el gerente más confiable de la experiencia, confirme que el usuario tiene suficiente dinero para comprar el art
Objetivo de armas
Los escenarios de combate requieren atención especial en la validación de los valores, particularmente a través de la validación de apuntado y golpe.
Imagine un juego en el que un jugador puede disparar un rayo láser a otro jugador. En lugar de que el cliente le diga al servidor quién debería dañar, debería decirle al servidor la posición de origen del disparo y la parte/posición que cree que ha golpeado. El servidor entonces puede validar lo siguiendo:
La posición que el cliente reporta dispara desde está cerca del personaje del jugador en el servidor. Tenga en cuenta que el servidor y el cliente difieren ligeramente debido a la latencia, por lo que se necesitará una tolerancia adicional.
La posición que el cliente reporta golpeando está razonablemente cerca de la posición de la parte que el cliente reporta golpeando, en el servidor.
No hay obstrucciones estáticas entre la posición que el cliente informa de disparar y la posición que el cliente informa de disparar. Esta verificación garantiza que un cliente no esté tratando de disparar a través de las paredes. Nota que esto solo debería verificar geometría estática para evitar que se rechace cualquier tiro debido a la latencia. Adicionalmente , es posible que quieras implementar validaciones adicionales del lado del servidor como las siguientes:
Rastrea cuando el jugador último disparó su arma y valida para asegurarte de que no estén disparando demasiado rápido.
Rastrea la cantidad de munición de cada jugador en el servidor y confirma que un jugador de disparo tiene suficiente munición para ejecutar el ataque de arma.
Si ha implementado un sistema de combate de equipos o un sistema de combate "jugadores contra bots", confirme que el personaje golpeado es un enemigo, no un compañero de equipo.
Confirmar que el jugador golpeado está vivo.
Almacene el estado del arma y del jugador en el servidor y confirme que un jugador que dispara no está bloqueado por una acción actual, como recargar o un estado como sprinting.
Manipulación de almacén de datos
En las experiencias que usan DataStoreService para guardar los datos del jugador, los piratas informáticos pueden aprovecharse de los datos inválidos Class.DataStore y métodos más oscuros para evitar que un DataStore se guarde correctamente. Esto puede ser especialmente abusado en las experiencias con intercambio de artículos, mercados y similares métodos, donde los artículos o inventariomoneda de
Asegúrese de que cualquier acción que se realice a través de un RemoteEvent o RemoteFunction que afecte los datos del jugador con la entrada del cliente se lleve a cabo de acuerdo con lo siguiendo:
- Instance valores no se pueden serializar en un DataStore y fallarán. Utilice válido de tipo para evitar esto.
- DataStores tienen límite de datos . Las cuerdas de cualquier longitud deben ser revisadas y/o limitadas para evitar esto, junto con asegurar que no se pueden agregar cadenas de claves aleatorias a las tablas por el cliente.
- Los índices de las tablas no pueden ser NaN o nil . Items over all las pase por el cliente y verificar que todos los índices son válidos.
- DataStores sólo puede aceptar caracteres UTF-8 válidos, por lo que deberías sanificar todos los caracteres proporcionados por el cliente a través de utf8.len() para aseg
Acelerador remoto
Si un cliente es capaz de hacer que su servidor complete una operación costosa computacionalmente o acceda a un servicio de tarifas limitadas como DataStoreService a través de un servicio de RemoteEvent , es crucial que implemente limitación de tarifas para garantizar que la operación no se llame demasiado a menudo. La limitación de tarifas se puede implementar rastreando
Validación de movimiento
Para experiencias competitivas, es posible que desee validar los movimientos del personaje del jugador en el servidor para asegurarse de que no se teletransporten alrededor del mapa o se muevan más rápido de lo que es aceptable.
En incrementos de 1 segundo, compruebe la nueva ubicación del personaje contra una ubicación anteriormente almacenada.
Compare la distancia real contra la tolerable y proceeda como sigue:
- Para un Delta tolerable, almacena la nueva ubicación del personaje en preparación para el próximo incremento de verificar, comprobar.
- Para un Delta (velocidad/teletransporte potencialmente código malicioso, vulnerabilidad del sistema):
- Aumenta el valor de "number of offenses" separado para el jugador, en lugar de sancionarlo por un "falso positivo" que resulta de la latencia extrema del servidor o otros factores no de explotación.
- Si ocurre un gran número de ofensas durante un período de 30-60 segundos, Kick() el jugador de la experiencia por completo; de lo contrario, restablezca la cuenta de "número de ofensas". Nota que al expulsar a un jugador por sospecha de engaño, es mejor que la práctica de registro del evento para que pueda rastrear cuántos jugadores están afectados.