Tactiques de sécurité et mitigation des cheats

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

Roblox utilise un système physique distribué dans lequel les clients ont la garde sur la simulation physique des objets dans leur contrôle, généralement le personnage du joueur et les objets non ancrés près de ce personnage. De plus, en utilisant le logiciel tiers, les exploiteurs peuvent exécuter du code Lua arbitraire sur le client pour manipuler le modèle de données de leur client et décompiler et afficher le code s'exécutant sur ce dernier.

Collectivement, cela signifie qu'un exploiteur expérimenté peut potentiellement exécuter du code pour tricher dans votre jeu, y compris :

  • Téléportation de leur propre personnage autour de l'emplacement.
  • Tirer RemoteEvents ou invoquer RemoteFunctions, tels que pour se donner des objets sans les gagner.
  • Ajustement de leur personnage de WalkSpeed afin qu'il se déplace vraiment rapidement.

Bien que vous puissiez implémenter des défenses de conception limitées pour attraper les attaques courantes, il est hautement recommandé que vous implémentiez plus de tactiques de mitigation côté serveur, car le serveur est l'autorité ultime pour toute expérience en cours.

Tactiques de conception défensive

Les décisions de conception de base peuvent servir de mesures de sécurité "première étape" pour décourager les exploits. Par exemple, dans un jeu de tir à la tête où les joueurs obtiennent des points pour tuer d'autres joueurs, un exploiter peut créer un tas de robots qui se téléportent dans le même endroit pour qu'ils puissent être rapidement tués pour des points. Donné cet exploit potentiel, considérez deux approches et leur résultat prédvisible :

ApprocheRésultat prévisible
Poursuivez les robots en écriant du code qui essaie de les détecter.
Réduisez ou supprimez entièrement les points de gain de points pour les victimes sur les nouveaux joueurs.

Bien que le design défensif ne soit évidemment pas une solution parfaite ou complète, il peut contribuer à une approche de sécurité plus large, ainsi que mitigation côté serveur.

Mitigation côté serveur

Aussi loin que possible, le serveur doit rendre le verdict final sur ce qui est «vrai» et sur lequel se trouve l'état actuel du monde. Les clients peuvent, bien sûr, demander au serveur de faire des modifications ou d'effectuer une action, mais le serveur doit valider et approuver chacune de ces modifications/actions avant que les résultats ne soient répliqués à d'autres joueurs.

Avec l'exception de certaines opérations physiques, les modifications du modèle de données sur le client ne se répliquent pas sur le serveur, de sorte que le chemin d'attaque principal est souvent via les événements réseau que vous avez déclarés avec RemoteEvents et RemoteFunctions. N'oubliez pas que un exploiter exécutant son propre code sur votre client peut les invoquer avec les données que ils veulent.

Validation du type d'exécution à distance

Un chemin d'attaque est pour qu'un exploiter invoque RemoteEvents et RemoteFunctions avec des arguments du mauvais taper. Dans certains scénarios, cela peut causer le code sur le serveur à écouter ces télécommandes d'une manière avantageuse pour l'exploiter.

Lors de l'utilisation d'événements/fonctions à distance, vous pouvez empêcher ce type d'attaque en validant les types types des arguments passés sur le serveur. Le module t" , disponible ici, est utile pour le type checking dans cette manière. Par exemple, en supposant que le code du module existe comme un Class.ModuleScript nommé

LocalScript dans StarterPlayerScripts

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
-- Passer la couleur et la position de la partie lors de l'invocation de la fonction
local newPart = remoteFunction:InvokeServer(Color3.fromRGB(200, 0, 50), Vector3.new(0, 25, 0))
if newPart then
print("The server created the requested part:", newPart)
elseif newPart == false then
print("The server denied the request. No part was created.")
end
Script dans ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
local t = require(ReplicatedStorage:WaitForChild("t"))
-- Créez un type de validateur à l'avance pour éviter les surcharges inutiles
local createPartTypeValidator = t.tuple(t.instanceIsA("Player"), t.Color3, t.Vector3)
-- Créer une nouvelle partie avec les propriétés passées
local function createPart(player, partColor, partPosition)
-- Vérifier les arguments passés
if not createPartTypeValidator(player, partColor, partPosition) then
-- Retournez silencieusement « faux » si le type check échoue ici
-- Augmenter une erreur sans rechargement peut être abusé pour ralentir le serveur
-- Fournissez plutôt des commentaires clients !
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
-- Lier « createPart() » au rappel de la fonction à distance
remoteFunction.OnServerInvoke = createPart

Validation des données

Une autre attaque que les exploiteurs pourraient lancer est d'envoyer des types valides techniquement mais de les rendre extrêmement grands, longs ou autrement malformés. Par exemple, si le serveur doit effectuer une opération coûteuse sur une chaîne qui s'étire avec la longueur, un exploiteur pourrait envoyer une chaîne extrêmement grande ou malformée pour ralentir le serveur.

De même, les deux inf et NaN seront type() comme 2>number2>, mais les deux peuvent causer de graves problèmes si un exploiter les envoie et qu'ils ne sont pas gérés correctement via des fonctions telles que les suivre:


local function isNaN(n: number): boolean
-- NaN n'est jamais égal à lui-même
return n ~= n
end
local function isInf(n: number): boolean
-- Le nombre peut être -inf ou inf
return math.abs(n) == math.huge
end

Une autre attaque courante que les exploiteurs peuvent utiliser implique l'envoi de tables au lieu d'un Instance . Les complexes de chargement peuvent imiter ce qui serait autrement une référence d'objet ordinaire.

Par exemple, fourni avec un système dans-l'expérience shop où les données d'objets comme les prix sont stockés dans NumberValue objets, un exploiter peut contourner tous les autres contrôles en faisant les choses suivre:

LocalScript dans 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, -- Les valeurs négatives peuvent également être utilisées, ce qui donne de la monnaie plutôt que de la prendre !
},
}
-- Envoyez un malicious payload au serveur (cela sera rejeté)
print(buyItemEvent:InvokeServer(payload)) -- Affiche « faux Invalid item provided »
-- Envoyez un vrai objet au serveur (cela passera !)
print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Outputs "true" and remaining currency if purchase succeeds
Script dans ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- Vérifiez si l'élément passe est non faux et se trouve dans le dossier ItemData
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Invalid item provided"
end
-- Le serveur peut alors continuer à traiter l'achat en fonction du flux d'exemple ci-dessous
end
-- Lier « buyItem » à l'événement de rappel de la fonctionnalité
buyItemEvent.OnServerInvoke = buyItem

Validation des valeurs

En plus de valider les types et les données , vous devez valider les valeurs transmises par 1> Class.RemoteEvent|RemoteEvents1> et 4> Class.RemoteFunction|RemoteFunctions4>, afin qu'elles soient valides et logiques dans le contexte demandé. Deux exemples communs sont un 7> dans-

Boutique d'expérience

Envisagez un système de boutique dans l'expérience avec un menu d'utilisateur, par exemple un menu de sélection de produit avec un bouton « Acheter ». Lorsque le bouton est pressé, vous pouvez invoquer un RemoteFunction entre le client et le serveur pour demander l'acheter. Cependant, il est important que le serveur, le gestionnaire le plus fiable de l'expérience, confirme que l'utilisateur a as

Example purchase flow from client to server through a RemoteEvent
Exemple de flux d'achat du client au serveur via un RemoteFunction

Ciblage d'arme

Les scénarios de combat nécessitent une attention spéciale pour valider les valeurs, en particulier en visant et en validant l'alignement.

Imaginez un jeu où un joueur peut tirer un rayon laser sur un autre joueur. Au lieu que le client indique au serveur qui est en train de nuire, il devrait plutôt indiquer au serveur la position d'origine du tir et la position/position qu'il pense avoir touchée. Le serveur peut alors valider le suivre:

  • La position que le client rapporte tirer depuis est près du personnage du joueur sur le serveur. Remarquez que le serveur et le client diffèrent légèrement en raison de la latence, il sera donc nécessaire d'appliquer une tolérance supplémentaire.

  • La position que le client rapporte « frapper » est raisonnablement proche de la position de la pièce que le client rapporte frappée, sur le serveur.

  • Il n'y a pas d'obstacles statiques entre la position que le client rapports de tir et la position que le client rapports de tir. Ce check garantit qu'un client ne tente pas de tirer à travers les murs. Remarquez que cela ne devrait vérifier que la géométrie statique pour éviter les tirs rejetés en raison de la latence. En outre , vous voudrez peut-être implémenter d'autres validations côté serveur comme suivant :

  • Suivez quand le joueur a tiré son arme pour la dernière fois et validez pour vous assurer qu'il ne tire pas trop vite.

  • Suivez le nombre de munitions de chaque joueur sur le serveur et confirmez qu'un joueur tirant a suffisamment de munitions pour exécuter l'attaque d'arme.

  • Si vous avez implémenté un système de combat «joueurs contre bots», ou si vous avez implémenté un système de chat «joueurs contre bots», confirmez que le personnage de coup est un ennemi, non pas un coéquipier.

  • Confirmez que le joueur touché est vivant.

  • Stockez l'état de l'arme et du joueur sur le serveur et confirmez qu'un joueur qui tire n'est pas bloqué par une action en cours, telle que le rechargement ou un état comme le sprint.

Manipulation de la base de données

Dans les expériences utilisant DataStoreService pour enregistrer les données des joueurs, les exploiteurs peuvent profiter des données invalides dans les données et de méthodes plus obscures pour empêcher un DataStore de sauvegarder correctement. Cela peut être spécialement abusé dans les expériences avec des échanges d'objets, des marchés et similaires où des articles ou de la monnaie quitt

Assurez-vous que toute action exécutée via un RemoteEvent ou un RemoteFunction qui affecte les données du joueur avec l'entrée du client est sanitisé en fonction de ce qui suivre:

  • Instance les valeurs ne peuvent pas être serialisées dans un DataStore et échoueront. Utilisez la validation du type pour éviter cela.
  • DataStores ont limites de données . Les chaînes de longueur arbitraire doivent être vérifiées et/ou limitées pour éviter cela, ainsi que s'assurer que les clés arbitraires de longueur illimitée ne peuvent pas être ajoutées aux tables par le client.
  • Les index de table ne peuvent pas être NaN ou nil . Iterate sur toutes les tables passées par le client et vérifier que tous les index sont valides.
  • DataStores ne peut accepter que des caractères UTF-8 valides, donc vous devriez saniser tous les scripts fournis par le client via utf8.len() pour vous assurer qu'ils sont valides.

Accélération à distance

Si un client est capable de rendre votre serveur complet une opération coûteuse en calcul, ou accéder à un service limité par abonnement comme DataStoreService via un RemoteEvent , il est crucial que vous implémentiez limite de taux pour garantir que l'opération n'est pas appelée trop souvent. Limite de taux peut être implémenté en suivant le temps auquel le

Validation de déplacement

Pour les expériences compétitives, vous voudrez peut-être valider les mouvements de personnage du joueur sur le serveur pour vous assurer qu'ils ne se téléportent pas autour de la carte ou ne se déplacent pas plus vite que ce qui est acceptable.

  1. Dans des increments de 1 seconde, vérifiez la nouvelle position du personnage par rapport à un endroit précédemment mis en cache.

    Image showing moving character's position on a straight path in increments of 1 second
  2. Déterminez un changement de distance maximum "tolérable" en fonction de la valeur de caractère WalkSpeed (studs par seconde), multiplié par ~1,4 pour permettre une certaine tolérance contre la latence du serveur. Par exemple, à une valeur par défaut de WalkSpeed, une tolérance de Delta est de ~22.

    Image showing tolerable change in distance based on character's walk speed
  3. Comparez la vraie distance décalage contre le décalage tolérable et procédez comme suivant :

    • Pour un delta tolérable, cachez la nouvelle position du personnage en préparation pour la prochaine augmentation de vérifier.
    • Pour une Delta inattendue ou insensible (potentielle exploit de vitesse/téléportation) :
      1. Augmente une valeur séparée « nombres d'infractions » pour le joueur, plutôt que de le pénaliser pour un « faux positif » résultant de la latence extrême ou d'autres facteurs non exploits.
      2. Si un grand nombre d'infractions se produit sur une période de 30-60 secondes, Kick() le joueur de l'expérience entièrement ; sinon, réinitialisez le nombre d'infractions « . Notez que lorsque vous expulsez un joueur pour tricher, il est préférable de noter l'événement pour que vous puissiez suivre le nombre d'infractions affectées.