Roblox utilise un système de physique distribué dans lequel les clients ont la garde de la simulation physique des objets dans leur contrôle, généralement le personnage du joueur et des objets non ancrés près de ce personnage.En outre, en utilisant un logiciel tiers, les exploiteurs peuvent exécuter du code Luau arbitraire sur le client pour manipuler le modèle de données du client et décompiler et afficher le code s'y exécutant.
Collectivement, cela signifie qu'un exploiteur qualifié peut potentiellement exécuter du code pour tricher dans votre jeu, y compris :
- Téléporter leur propre personnage autour de l'emplacement.
- Tirer sur sécurisé RemoteEvents ou invoquer RemoteFunctions , comme pour attribuer des articles sans les gagner.
- Ajuster leur caractère WalkSpeed afin qu'il se déplace très rapidement.
Bien que vous puissiez mettre en œuvre des défenses de conception limitées pour capturer les attaques communes, il est fortement recommandé d'implémenter des tactiques de mitigation plus fiables côté serveur, car le serveur est l'autorité ultime pour toute expérience d'exécution.
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 tireur où les joueurs obtiennent des points pour tuer d'autres joueurs, un exploiteur peut créer un groupe de bots qui se téléportent au même endroit pour qu'ils puissent être rapidement tués pour des points.Compte tenu de cet exploit potentiel, envisagez deux approches et leur résultat prévisible :
Approche | Résultat prévisible |
---|---|
Poursuivez les bots en écrivant du code qui tente de les détecter. | |
Réduire ou éliminer définitivement les gains de points pour les tueries sur les joueurs nouvellement apparus. |
Bien que le design défensif ne soit évidemment pas une solution parfaite ou complète, il peut contribuer à une approche plus large de la sécurité, ainsi qu'à la mitigation côté serveur.
Atténuation côté serveur
Dans la mesure du possible, le serveur devrait rendre le verdict final sur ce qui est "vrai" et quel est 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 devrait valider et approuver chacune de ces modifications/actions avant que les résultats ne soient répliqués vers d'autres joueurs.
A l'exception de certaines opérations de physique, les modifications du modèle de données sur le client ne se répliquent pas sur le serveur, de sorte que le principal chemin d'attaque est souvent via les événements réseau que vous avez déclarés avec RemoteEvents et RemoteFunctions .Rappelez-vous qu'un exploiteur exécutant son propre code sur votre client peut les invoquer avec n'importe quelle donnée qu'il souhaite.
Validation du type d'exécution à distance
Un chemin d'attaque est pour qu'un exploiteur invoque RemoteEvents et RemoteFunctions avec des arguments du mauvais taper.Dans certains scénarios, cela peut provoquer une erreur du code sur le serveur qui écoute ces télécommandes d'une manière qui est avantageuse pour l'exploiteur.
Lors de l'utilisation d'événements/fonctions à distance, vous pouvez empêcher ce type d'attaque en validant les types de paramètres passés sur le serveur.Le module "t" , disponible ici, est utile pour vérifier le type de cette manière.Par exemple, en supposant que le code du module existe sous le nom t à l'intérieur de : :
LocalScript dans StarterPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")-- Passez la couleur et la position de la partie lors de l'invocation de la fonctionlocal 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 dans ServerScriptService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
local t = require(ReplicatedStorage:WaitForChild("t"))
-- Créer un valideur de type en 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 le type des arguments passés
if not createPartTypeValidator(player, partColor, partPosition) then
-- Retournez silencieusement "faux" si la vérification du type échoue ici
-- Élever une erreur sans temps de recharge peut être abusé pour ralentir le serveur
-- Fournir des commentaires clients à la place !
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()" à la fonction de 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 techniquement valides mais de les rendre extrêmement grands, longs ou autrement déformés.Par exemple, si le serveur doit effectuer une opération coûteuse sur une chaîne qui s'adapte à la longueur, un exploiteur pourrait envoyer une chaîne incroyablement grande ou malformée pour saturer le serveur.
De même, tanto inf et NaN provoqueront type() comme number , mais les deux peuvent causer des problèmes majeurs si un exploiteur 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 pourrait être -inf ou inf
return math.abs(n) == math.huge
end
Une autre attaque commune que les exploiteurs peuvent utiliser consiste à envoyer tables à la place d'un Instance.Les charges utiles complexes peuvent imiter ce qui serait autrement une référence d'objet ordinaire.
Par exemple, fourni avec un système de boutique en expérience où les données d'objets comme les prix sont stockées dans des objets NumberValue, un exploiteur peut contourner toutes les autres vérifications en faisant ce qui 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, -- Des valeurs négatives pourraient également être utilisées, ce qui entraînerait donner de la monnaie plutôt que de la prendre !},}-- Envoyer un payload malveillant au serveur (cela sera rejeté)print(buyItemEvent:InvokeServer(payload)) -- Affiche « faux élément non valide fourni »-- Envoyez un objet réel 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 passé n'est pas spoofé 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 ensuite traiter l'achat en se basant sur le flux d'exemple ci-dessous
end
-- Lier "buyItem()" au rappel de la fonction à distance
buyItemEvent.OnServerInvoke = buyItem
Validation de valeur
En plus de valider les types et les données valides, vous devez valider les valeurs transmises via et , vous assurant qu'elles sont valides et logiques dans le contexte demandé.Deux exemples communs sont un magasin d'expérience et un système de ciblage d'armes .
boutiquedans l'expérience
Considérez un système de boutique dans l'expérience avec une interface utilisateur, par exemple un menu de sélection de produits avec un bouton « Acheter ».Lorsque le bouton est pressé, vous pouvez invoquer un RemoteFunction entre le client et le serveur pour demander l'achat.Cependant, il est important que le serveur , le gestionnaire le plus fiable de l'expérience, confirme que l'utilisateur a assez d'argent pour acheter l'item.

Ciblage d'armes
Les scénarios de combat méritent une attention spéciale sur la validation des valeurs, notamment via la visée et la validation des coups.
Imaginez un jeu où un joueur peut tirer un rayon laser sur un autre joueur.Plutôt que le client d'indiquer au serveur qui endommager, il devrait plutôt indiquer au serveur la position d'origine du tir et la partie/position qu'il pense avoir touchée.Le serveur peut ensuite valider ce qui suivre:
La position que le client signale tirant de est près du personnage du joueur sur le serveur.Notez que le serveur et le client différeront légèrement en raison du délai, une tolérance supplémentaire devra donc être appliquée.
La position que le client signale frappant est raisonnablement proche de la position de la partie que le client signale frappant, sur le serveur.
Il n'y a pas d'obstacles statiques entre la position depuis laquelle le client rapporte le tir et la position vers laquelle le client rapporte le tir.Cette vérification garantit qu'un client n'essaie pas de tirer à travers les murs.Notez que cela ne devrait vérifier la géométrie statique que pour éviter que des tirs valides soient rejetés en raison du délai. En plus , vous pouvez souhaiter implémenter d'autres validations côté serveur comme suivant :
Suivez quand le joueur a dernièrement tiré son arme et validez pour vous assurer qu'il ne tire pas trop vite.
Suivez la quantité de munitions de chaque joueur sur le serveur et confirmez qu'un joueur en feu a suffisamment de munitions pour exécuter l'attaque d'armes.
Si vous avez implémenté équipes ou un système de combat «joueurs contre bots», confirmez que le personnage touché est un ennemi, 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 en feu n'est pas bloqué par une action actuelle 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 du joueur, les exploiteurs peuvent profiter de données invalides et de méthodes plus obscures pour empêcher un DataStore de se sauver correctement.Cela peut être abusé en particulier dans les expériences de trading d'objets, de marchés et de systèmes similaires où des objets ou de la monnaie quittent l'inventaire d'un joueur.
Assurez-vous que toutes les actions effectuées via un RemoteEvent ou RemoteFunction qui affectent les données du joueur avec l'entrée du client sont sanitized en fonction du suivre:
- Instance les valeurs ne peuvent pas être sérialisées en DataStore et échoueront. Utilisez la validation du type pour empêcher cela.
- DataStores a des limites de données.Les chaînes de longueur arbitraire doivent être vérifiées et/ou plafonnées pour éviter cela, tout en veillant à ce que les clés arbitraires illimitées ne puissent pas être ajoutées aux tables par le client.
- Les index de table ne peuvent pas être NaN ou nil. Itérer sur toutes les tables transmises par le client et vérifier que tous les index sont valides.
- DataStores ne peut accepter que des caractères UTF-8 valides, vous devez donc sanitiser toutes les chaînes fournies par le client via utf8.len() pour vous assurer qu'elles sont valides. utf8.len() retournera la longueur d'une chaîne, traitant les caractères unicode comme un seul caractère ; si un caractère UTF-8 non valide est rencontré, il retournera nil et la position du caractère non valide.Notez que des chaînes UTF-8 invalides peuvent également être présentes dans les tables en tant que clés et valeurs.
Ralentissement à distance
Si un client est capable de faire en sorte que votre serveur effectue une opération coûteuse en termes de calcul ou accède à un service limité en termes de fréquence comme DataStoreService via un RemoteEvent, il est crucial que vous mettiez en œuvre la limitation des taux pour vous assurer que l'opération n'est pas appelée trop souvent.La limitation du taux peut être mise en œuvre en suivant quand le client a invoqué pour la dernière fois un événement distant et en rejetant la prochaine demande si elle est appelée trop tôt.
Validation du mouvement
Pour les expériences compétitives, vous pouvez souhaiter valider les mouvements du 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.
En augmentations de 1 seconde, vérifiez la nouvelle localisation du personnage contre un emplacement précédemment enregistré.
Comparez le delta de distance réel contre le delta tolérable et procédez comme suit :
- Pour un delta tolérable, cachez la nouvelle localisation du personnage en préparation de la prochaine vérifieraugmentée.
- Pour un delta imprévu ou intolérable (exploit de vitesse/téléportation potentiel) :
- Augmenter une valeur séparée de « nombre d'infractions » pour le joueur, versus de le pénaliser pour un « faux positif » résultant d'une latence extrême du serveur ou d'autres facteurs non exploitables.
- Si un grand nombre d'infractions se produisent 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 fraude, il est recommandé d'enregistrer l'événement afin de pouvoir suivre le nombre de joueurs touchés.