Jedes Mal, wenn ein Client eine Aktion auf dem Server auslösen kann, besteht das Potenzial für Missbrauch. Während RemoteEvents und RemoteFunctions hier die häufigsten Angriffsvektoren sind, sind auch andere Instanzen wie ProximityPrompt anfällig. Dieser Abschnitt behandelt, wie man diese Grenze absichert.
Validierung von Remote-Eingaben
Jedes Stück Daten, das von einem Client gesendet wird, muss vom Server validiert werden, bevor es verwendet wird. Wenden Sie diese Validierungsschichten basierend darauf an, was das Remote tut.
Kontext-/Berechtigungsvalidierung
Dies ist notwendig für Remotes, die den Zustand des Spiels, den Fortschritt oder andere Spieler beeinflussen. Der Server muss überprüfen, ob der Spieler die erforderlichen Berechtigungen hat, um die Anfrage zu stellen. Zum Beispiel, ist der Spieler nah genug an einem Geschäft, um etwas zu kaufen? Hat er einen Schlüssel, der erforderlich ist, um eine Tür zu öffnen? Ist sein Charakter lebendig?
Die Validierung ist notwendig für Remotes, die:
- Belohnungen gewähren oder den Fortschritt des Spielers ändern
- Andere Spieler oder den gemeinsamen Spielzustand beeinflussen
- Aktionen mit Distanz-, Zeit- oder Berechtigungsanforderungen ausführen (zum Beispiel, Geschäftsdistanz)
Typ- und Strukturvalidierung
Ein weiterer Angriff, den Ausnutzer starten könnten, besteht darin, technisch gültige Typen zu senden, diese jedoch extrem groß, lang oder anderweitig fehlerhaft zu machen. Wenn der Server beispielsweise eine teure Operation an einem String durchführen muss, die mit der Länge skaliert, könnte ein Ausnutzer einen unglaublich großen oder fehlerhaften String senden, um den Server zu überlasten.
Ein weiterer häufiger Angriff, den Ausnutzer verwenden könnten, besteht darin, tables anstelle eines Instance zu senden. Komplexe Payloads können das nachahmen, was ansonsten ein gewöhnlicher Objektverweis wäre.
Zum Beispiel, wenn ein In-Game-Shop-System bereitgestellt wird, in dem die Itemdaten wie Preise in NumberValue-Objekten gespeichert sind, könnte ein Ausnutzer alle anderen Überprüfungen umgehen, indem er Folgendes tut:
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, -- Negative Werte könnten ebenfalls verwendet werden, was dazu führt, dass Währung gegeben wird, anstatt sie zu nehmen!
},
}
-- Sendet schädliche Payload an den Server (dies wird abgelehnt)
print(buyItemEvent:InvokeServer(payload)) -- Gibt "false Invalid item provided" aus
-- Sendet ein echtes Item an den Server (dies wird durchgehen!)
print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Gibt "true" und verbleibende Währung aus, wenn der Kauf erfolgreich istlocal ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- Überprüfen, ob das übergebene Item nicht gefälscht ist und im ItemData-Ordner ist
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Invalid item provided"
end
-- Der Server kann dann den Kauf basierend auf dem folgenden Beispielablauf verarbeiten
end
-- Bindet "buyItem()" an den Callback der Remote-Funktion
buyItemEvent.OnServerInvoke = buyItemWertvalidierung
Zusätzlich zur Validierung von Typen und Daten sollten Sie die Werte validieren, die durch RemoteEvents und RemoteFunctions übergeben werden, um sicherzustellen, dass sie im angeforderten Kontext gültig und logisch sind. Dies ist entscheidend für Remotes, die Währung, Items, numerische Eingaben oder benutzergenerierte Inhalte behandeln. Sie müssen sicherstellen, dass die Werte innerhalb der erwarteten Grenzen liegen, zum Beispiel, dass die Kaufmenge größer als null ist oder dass eine Item-ID einem echten Item in Ihrem Spiel entspricht.
Beachten Sie, dass sowohl inf als auch NaN gültige Zahlentypen sind, aber beide können große Probleme verursachen, wenn ein Ausnutzer sie sendet und sie nicht korrekt behandelt werden. Verwenden Sie math.isfinite(), um sie zu erkennen und abzulehnen:
-- Lehnt numerische Werte ab, die NaN oder +/-inf sind
if not math.isfinite(offeredGold) then
return false, "Invalid number provided"
endDie Gefahr von NaN
Ein Ausnutzer kann NaN (Not a Number) als Argument senden. NaN ist besonders gefährlich, da es vom Typ "number" ist, aber alle Standardvergleiche fehlschlägt, was es ihm ermöglicht, logische Überprüfungen, die sicher erscheinen, subtil zu umgehen. Schauen wir uns ein anfälliges Handelssystem an, bei dem ein Spieler ein Angebot macht.
-- ANFÄLLIGER SERVERCODE
local function onCreateTradeOffer(player, offeredGold)
-- 1. TYPÜBERPRÜFUNG: Dies besteht! typeof(NaN) ist "number".
if typeof(offeredGold) ~= "number" then
return "Invalid offer"
end
-- 2. BEREICHSÜBERPRÜFUNG: Dies wird umgangen!
-- (NaN < 0) ist falsch. (NaN > 1000000) ist ebenfalls falsch. Die Überprüfung tut nichts.
if offeredGold < 0 or offeredGold > 1000000 then
return "Offer out of range"
end
-- 3. INVENTARÜBERPRÜFUNG: Dies wird umgangen!
-- (NaN > player.Gold.Value) ist falsch.
if offeredGold > player.Gold.Value then
return "Not enough gold"
end
-- ANFÄLLIGKEIT: Ein betrügerisches Handelsangebot mit NaN Gold wird erstellt!
createTrade(player, {gold = offeredGold})
return "Trade offer created."
endDer Ausnutzer hat erfolgreich ein Handelsangebot erstellt, ohne Gold zu haben, da jede Überprüfung stillschweigend fehlgeschlagen ist. Darüber hinaus wird dieser einzelne NaN-Wert nun jedes andere System, das versucht, ihn zu verwenden, vergiften und sogar auf andere Spieler durch das Angebot übertragen.
Im Folgenden finden Sie einige hochrangige Beispiele, die das sichere Design gängiger In-Game-Funktionen abdecken.
In-Game-Shop
Betrachten Sie ein In-Game-Shop-System mit einer Benutzeroberfläche, beispielsweise ein Produktwahlmenü mit einer "Kaufen"-Schaltfläche. Wenn die Schaltfläche gedrückt wird, können Sie eine RemoteFunction zwischen dem Client und dem Server aufrufen, um den Kauf anzufordern. Es ist jedoch wichtig, dass der Server, der zuverlässigste Verwalter des Spiels, bestätigt, dass der Benutzer genügend Geld hat, um den Artikel zu kaufen.

Waffenzielverfolgung
Kampfszenarien erfordern besondere Aufmerksamkeit bei der Validierung von Werten, insbesondere bei Ziel- und Treffervalidierungen.
Stellen Sie sich ein Spiel vor, in dem ein Spieler einen Laserstrahl auf einen anderen Spieler abfeuern kann. Anstatt dass der Client dem Server sagt, wen er beschädigen soll, sollte er stattdessen dem Server die Ursprungsposition des Schusses und das Teil/die Position mitteilen, die er glaubt, getroffen zu haben. Der Server kann dann Folgendes validieren:
- Die Position, von der der Client berichtet, dass er schießt, ist in der Nähe des Charakters des Spielers auf dem Server. Beachten Sie, dass sich der Server und der Client aufgrund von Latenz leicht unterscheiden, sodass zusätzliche Toleranz angewendet werden muss.
- Die Position, die der Client als getroffen meldet, ist auf dem Server in angemessener Nähe zur Position des Teils, das der Client als getroffen meldet.
- Es gibt keine statischen Hindernisse zwischen der Position, von der der Client berichtet, dass er schießt, und der Position, die der Client berichtet, dass er getroffen hat. Diese Überprüfung stellt sicher, dass ein Client nicht versucht, durch Wände zu schießen. Beachten Sie, dass dies nur statische Geometrie überprüfen sollte, um zu vermeiden, dass gültige Schüsse aufgrund von Latenz abgelehnt werden.
Darüber hinaus möchten Sie möglicherweise weitere serverseitige Validierungen implementieren, wie folgt:
- Verfolgen Sie, wann der Spieler zuletzt seine Waffe abgefeuert hat, und validieren Sie, um sicherzustellen, dass er nicht zu schnell schießt.
- Verfolgen Sie die Munitionsmenge jedes Spielers auf dem Server und bestätigen Sie, dass ein schießender Spieler genügend Munition hat, um den Waffenschuss auszuführen.
- Wenn Sie Teams oder ein "Spieler gegen Bots"-Kampfsystem implementiert haben, bestätigen Sie, dass der getroffene Charakter ein Feind und kein Teamkollege ist.
- Bestätigen Sie, dass der getroffene Spieler lebt.
- Speichern Sie den Zustand der Waffe und des Spielers auf dem Server und bestätigen Sie, dass ein schießender Spieler nicht durch eine aktuelle Aktion wie Nachladen oder einen Zustand wie Sprinten blockiert ist.
Ratenbegrenzung
Wann immer serverseitige Logik vom Client (über Remotes, Touched-Events, Proximity-Prompts, ClickDetectors usw.) ausgelöst werden kann, besteht die Möglichkeit, dass diese Logik von Ausnutzern oder sogar legitimen Benutzern gespammt wird. Die Ratenbegrenzung steuert, wie häufig diese Aktionen ausgeführt werden können, um Missbrauch und Systemüberlastung zu verhindern. Während es praktisch (und manchmal notwendig) ist, Ratenlimits auf dem Client zu implementieren, sollten Sie sich niemals ausschließlich auf ein clientseitiges Ratenlimit verlassen.
Für jede serverseitige Logik, die von einem Client ausgelöst werden kann, sollten Sie immer die maximale Rate berücksichtigen, mit der eine solche Logik ausgeführt werden sollte. Viele serverseitige Aktionen müssen in ihrer Häufigkeit begrenzt werden, um Missbrauch oder Fehler zu verhindern, wie zum Beispiel:
- Roblox API-Aufrufe: Backend-APIs wie DataStoreService und BadgeService haben eingebaute Ratenlimits, die dazu führen, dass Ihre Anfragen fehlschlagen, wenn sie überschritten werden
- Rechenintensive Operationen: Aktionen, die erhebliche Serverressourcen verbrauchen oder andere Clients beeinträchtigen
- Serverbeispiel: Klonen großer Modelle aus ServerStorage, selbst wenn sie nie an Clients repliziert werden
- Clientbeispiel: Alle verbundenen Clients anweisen, ihre GUI gleichzeitig zu aktualisieren
- Ausnutzbare Mechaniken: Aktionen, die missbraucht werden könnten, wenn sie schnell ausgeführt werden, wie das Gewähren von Unverwundbarkeit, das Teleportieren von Spielern oder das Vergeben von Währung
Token-Bucket-Beispiel
Ein robuster und gängiger Ansatz zur Ratenbegrenzung ist der Token-Bucket-Algorithmus. Stellen Sie sich vor, jeder Benutzer hat einen Eimer, der eine bestimmte Anzahl von Tokens halten kann. Um eine Aktion auszuführen, muss der Benutzer ein Token ausgeben. Der Eimer füllt sich mit neuen Tokens in einem gleichmäßigen Tempo. Diese Methode ermöglicht kurze Ausbrüche von Aktionen, wenn nötig, verhindert jedoch anhaltenden Spam, indem sie eine durchschnittliche Rate über die Zeit durchsetzt.
--!strict
-- Modul befindet sich in ServerScriptService
type UserId = number
type Bucket = {
tokens: number,
last: number,
}
type TokenBucketT = {
capacity: number,
refillPerSecond: number,
buckets: { [UserId]: Bucket },
allow: (self: TokenBucketT, userId: UserId) -> boolean,
}
local TokenBucket = {}
TokenBucket.__index = TokenBucket
-- Erstellt einen Limiter, der höchstens `capacity` Ereignisse zulässt, die mit einer Rate von
-- `capacity / windowSeconds` Tokens pro Sekunde wieder aufgefüllt werden.
function TokenBucket.new(capacity: number, windowSeconds: number): TokenBucketT
assert(capacity >= 1, "capacity must be >= 1")
assert(windowSeconds > 0, "windowSeconds must be > 0")
local self: TokenBucketT = {
capacity = capacity,
refillPerSecond = capacity / windowSeconds,
buckets = {},
}
return (setmetatable(self, TokenBucket) :: any) :: TokenBucketT
end
local function refill(b: Bucket, now: number, cap: number, rate: number)
local elapsed = now - b.last
if elapsed > 0 then
b.tokens = math.min(cap, b.tokens + elapsed * rate)
b.last = now
end
end
-- Gibt false zurück, wenn der Benutzer sein Limit überschreiten würde
function TokenBucket:allow(userId: UserId): boolean
local now = time()
local b = self.buckets[userId]
if not b then
b = { tokens = self.capacity, last = now }
self.buckets[userId] = b
else
refill(b, now, self.capacity, self.refillPerSecond)
end
if b.tokens >= 1 then
b.tokens -= 1
return true
end
return false
end
return TokenBucketUm das Modul zu verwenden, erstellen Sie zunächst eine neue Limiter-Instanz mit TokenBucket.new(capacity, windowSeconds). Die capacity ist die maximale Anzahl von Anfragen, die ein Benutzer in einem schnellen Ausbruch stellen kann, und die windowSeconds bestimmt, wie lange es dauert, um all diese Tokens wieder aufzufüllen. Zum Beispiel erstellt TokenBucket.new(5, 10) einen Limiter, der Ausbrüche von bis zu 5 Anfragen zulässt und alle zwei Sekunden (10 Sekunden / 5 Tokens) ein Token wieder auffüllt.
Bevor Sie geschützte Logik ausführen, rufen Sie die Methode :allow(userId) auf und lehnen Sie die Aktion ab, wenn sie false zurückgibt. Es ist auch eine gute Praxis, die Benutzerdaten des Buckets zu löschen, wenn sie das Spiel verlassen, um Speicherlecks zu vermeiden.
Das folgende Beispielskript zeigt, wie man ein RemoteEvent, das für ein benutzerdefiniertes Chatsystem verwendet wird, vor Spam durch Spieler schützt.
-- Beispielverwendung in einem Serverskript
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local TokenBucket = require(ServerScriptService.TokenBucket)
local ChatRemote = ReplicatedStorage:WaitForChild("ChatRemote") :: RemoteEvent
-- 5 Nachrichten pro 10 Sekunden (Kapazität 5, füllt sich mit 0,5 Tokens/Sekunde)
local chatLimiter = TokenBucket.new(5, 10)
ChatRemote.OnServerEvent:Connect(function(player: Player, message: string)
if not chatLimiter:allow(player.UserId) then
-- Zu schnell: Anfrage ablehnen oder eine Spam-Warnung senden
return
end
-- Nachricht verarbeiten (nach anderer Validierung)
broadcastMessage(player, message)
end)
-- Bereinigung zur Vermeidung von Speicherlecks
Players.PlayerRemoving:Connect(function(p: Player)
chatLimiter.buckets[p.UserId] = nil
end)Absicherung von client-triggered Instanzen
ProximityPrompts, ClickDetectors und DragDetectors sind keine Remotes, aber ein Ausnutzer kann deren Ereignisse aus beliebiger Entfernung und zu jeder Zeit auslösen, oft ohne Eigenschaften wie Enabled oder MaxActivationDistance zu beachten. Diese Objekte müssen mit der gleichen Strenge wie Remotes abgesichert werden.
ProximityPrompt
- Ein Ausnutzer kann jedes Ereignis auslösen, selbst wenn Enabled auf dem Server falsch ist.
- Enabled, MaxActivationDistance und RequiresLineOfSight können stillschweigend auf dem Client geändert werden, sodass jeder replizierte ProximityPrompt von jedem Standort aus gesehen und potenziell interagiert werden kann.
- Der Server akzeptiert Halteereignisse (wie PromptButtonHoldBegan), selbst wenn die serverseitige HoldDuration null ist. Der Client kann die HoldDuration manipulieren, um Interaktionen unnatürlich schnell auszuführen.
- Nur das Triggered-Ereignis hat eine serverseitige Distanzüberprüfung. Andere Ereignisse (wie PromptButtonHoldBegan und TriggerEnded) haben keine Distanzüberprüfung und können beliebig von jedem Client gesendet werden.
ClickDetector
- Es gibt keine Überprüfungen auf dem Server für irgendwelche Ereignisse.
- Ausnutzer können jedes Ereignis aus beliebiger Entfernung replizieren, selbst wenn der ClickDetector deaktiviert ist (MaxActivationDistance von 0 oder DragDetector.Enabled falsch).
- Ereignisse können repliziert werden, selbst wenn der ClickDetector nicht an etwas Klickbares angehängt ist.
DragDetector
- Für Ereignisse, die von ClickDetector abgeleitet sind, gibt es keine Überprüfungen.
- Für Ziehereignisse werden die Enabled-Eigenschaft und die PermissionPolicy-Eigenschaften überprüft und vom Server respektiert. Alle anderen Eigenschaften werden nicht überprüft.
Wenn eines dieser Ereignisse ausgelöst wird, sollte Ihr Serverskript seine eigene Validierung durchführen, bevor es eine Aktion ausführt:
- Überprüfen, ob es aktiviert ist: Stellen Sie sicher, dass die Instanz beabsichtigt ist, aktiviert zu werden, indem Sie ihre Eigenschaften (wie Enabled, HoldDuration, RunLocally usw.) auf dem Server überprüfen. Dieser Schritt ist nicht erforderlich, wenn das Objekt immer aktiviert ist.
- Überprüfen des Spielerstatus: Stellen Sie sicher, dass der Spieler die Aktion ausführen kann, gegebenenfalls den aktuellen Status des Spielers. Ist der Charakter des Spielers in der Welt? Ist sein Charakter nah genug am Objekt? Muss der Spieler lebendig sein, um mit diesem Objekt zu interagieren? Überprüfen Sie alle anderen Spielerstatus, die von Ihrem Spiel auf dem Server verwaltet werden, nach Bedarf.
- Ratenbegrenzung anwenden: Wie in Ratenbegrenzung behandelt, wenden Sie Abkühlzeiten auf dem Client an und setzen Sie Ratenlimits auf dem Server durch, um zu verhindern, dass diese Ereignisse gespammt und missbraucht werden. Für Interaktionen, die voraussichtlich eine Mindestzeit in Anspruch nehmen, stellen Sie sicher, dass die Clients sie nicht zu schnell abschließen.
Hinweis zur Netzwerkbesitzschaft: Standardmäßig, wenn eines dieser Objekte Kinder von nicht verankerten Teilen oder einer nicht verankerten Baugruppe sind, kann ein Ausnutzer die Netzwerkbesitzschaft der übergeordneten Teile übernehmen und sie direkt zu seinem Charakter bewegen, wodurch Distanzüberprüfungen umgangen werden. Für kritische Aktionen verwenden Sie verankerte Teile oder die Netzwerkbesitzschaft-API zusammen mit der erforderlichen serverseitigen Validierung.
RemoteEvents und RemoteFunctions
Begrenzen Sie den Umfang und die Auswirkungen von RemoteFunctions und RemoteEvents. Seien Sie äußerst vorsichtig beim dynamischen Laden von Assets (sogar Texturen oder Sounds) oder beim Ausführen von Spielcode (insbesondere über require) basierend auf den Argumenten von Remote-Funktionen/Ereignissen. Vermeiden Sie die Implementierung von Remotes, die es einem Client ermöglichen, einen beliebigen Pfad oder eine Instanzreferenz anzugeben, die der Server löschen oder ändern soll, selbst wenn solche Änderungen trivial erscheinen. Remotes, die den Zustand beliebiger Instanzen ändern oder weitreichende Änderungen am DataModel-Baum vornehmen können, können oft mit anderen Fehlern verkettet werden, um den Gesamtzustand oder die Logik erheblich zu beeinträchtigen. Überprüfen Sie nicht nur den Typ der Instanzargumente, sondern auch die Klasse und den erwarteten Standort oder die Struktur innerhalb des DataModels.
Client-zu-Client-Kommunikation
Einige Remotes sind so konzipiert, dass ein Client Effekte bei anderen Clients auslösen kann. Dies geschieht, wenn ein Client ein Remote an den Server sendet, der dann RemoteEvent:FireAllClients(), RemoteEvent:FireClient() oder ähnliche Methoden verwendet, um Informationen weiterzuleiten. Dieses Muster ist gefährlich, wenn es nicht ordnungsgemäß gesichert ist - der Server muss ein Torwächter sein, nicht nur ein Relais.
Stellen Sie sich ein Szenario vor, in dem Spieler einen Blitzzauber wirken können. Wenn er gewirkt wird, sehen nahegelegene Spieler den Blitz, hören den Klang und erleben eine Kamerawackel.
Ein anfälliges Serverskript könnte so aussehen:
local castLightningEvent = game.ReplicatedStorage.CastLightning
-- Dieses Serverskript leitet die Nachricht an alle weiter, ohne Validierung
castLightningEvent.OnServerEvent:Connect(function(player, strikePosition)
castLightningEvent:FireAllClients(strikePosition)
end)local castLightningEvent = game.ReplicatedStorage.CastLightning
-- Erstellen Sie visuelle und Soundeffekte auf dem Client
local function createLightningEffect(strikePosition)
-- Code zum Wackeln der Kamera
-- Code zum Abspielen eines lauten Blitzgeräuschs
-- Code zum Erstellen eines visuellen Blitzes
print("Blitz schlägt ein bei: " .. tostring(strikePosition))
end
-- Wenn der Server das Ereignis überträgt, führt dieser Client den Effekt aus
castLightningEvent.OnClientEvent:Connect(createLightningEffect)Ein Ausnutzer kann dieses anfällige System missbrauchen, indem er:
- Spam: Das Remote-Hunderten von Malen pro Sekunde aufruft, was kontinuierliche Effekte verursacht, die das Spiel unspielbar machen
- Ungültige Daten: nil, NaN oder falsche Typen sendet, die Skriptfehler bei allen Clients verursachen
- Unbefugte Nutzung: Zauber wirken, die sie nicht freigeschaltet haben oder für die sie keine Ressourcen haben
Der Server muss ein Torwächter sein, nicht ein Relais. Bevor er überträgt, muss er die Anfrage validieren und eine Ratenbegrenzung pro Spieler anwenden. Hier ist ein Beispiel für einige Methoden, die zur Validierung der Anfrage eingesetzt werden könnten.
local castLightningEvent = game.ReplicatedStorage.CastLightning
local playerCooldowns = {}
local COOLDOWN_TIME = 3
castLightningEvent.OnServerEvent:Connect(function(player, strikePosition)
-- 1. Typvalidierung
if typeof(strikePosition) ~= "Vector3" then
return -- Ungültiger Typ, stillschweigend ablehnen
end
-- 2. Überprüfen auf NaN (NaN ~= NaN ist die einzige Möglichkeit, es zu erkennen)
if strikePosition.X ~= strikePosition.X then
return -- Enthält NaN, ablehnen
end
-- 3. Beispiel für Ratenbegrenzung
local lastCast = playerCooldowns[player] or 0
if tick() - lastCast < COOLDOWN_TIME then
return -- Immer noch auf Abkühlung
end
-- 4. Beispiel für Berechtigungsprüfung
if not player:GetAttribute("HasLightningSpell") then
return -- Spieler hat diesen Zauber nicht
end
-- 5. Beispiel für Reichweitenvalidierung
local character = player.Character
local humanoidRootPart = character and character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then
return
end
local distance = (humanoidRootPart.Position - strikePosition).Magnitude
if distance > 100 then
return -- Außerhalb der Reichweite
end
-- Alle Überprüfungen bestanden - sicher zu übertragen
playerCooldowns[player] = tick()
castLightningEvent:FireAllClients(player, strikePosition)
end)Durch die Implementierung dieser Überprüfungen wird es für einen Ausnutzer schwieriger, das Spiel für andere Spieler zu ruinieren. Der Server fungiert als schützende Barriere und stellt sicher, dass nur gültige, autorisierte Aktionen an andere Clients übertragen werden.
Besondere Überlegungen
Die folgenden sind einige zusätzliche Fälle, die Sie berücksichtigen sollten und die möglicherweise eine spezielle Behandlung erfordern.
Manipulation des Datenspeichers
In Spielen, die DataStoreService verwenden, um Spieldaten zu speichern, können Ausnutzer ungültige Daten, Rennbedingungen ausnutzen, um Speicher zu beschädigen oder Gegenstände in einem DataStore zu duplizieren. Dies ist besonders problematisch in Spielen mit Item-Handel, Marktplätzen und Währungssystemen.
Stellen Sie sicher, dass alle Aktionen, die über ein RemoteEvent oder RemoteFunction ausgeführt werden und die Spieldaten mit Client-Eingaben beeinflussen, basierend auf Folgendem bereinigt werden:
- Instanzwerte können nicht in einem DataStore serialisiert werden und schlagen fehl. Verwenden Sie die Typvalidierung, um dies zu verhindern.
- DataStores haben Datenlimits. Strings beliebiger Länge sollten überprüft und/oder begrenzt werden, um dies zu vermeiden, und sicherstellen, dass keine unbegrenzten beliebigen Schlüssel von Clients zu Tabellen hinzugefügt werden können.
- Tabellenindizes können nicht NaN oder nil sein. Iterieren Sie über alle Tabellen, die vom Client übergeben werden, und überprüfen Sie, ob alle Indizes gültig sind.
- DataStores können nur gültige UTF-8-Zeichen akzeptieren, daher sollten Sie alle vom Client bereitgestellten Strings über utf8.len() bereinigen, um sicherzustellen, dass sie gültig sind. utf8.len() gibt die Länge eines Strings zurück und behandelt Unicode-Zeichen als ein einzelnes Zeichen; wenn ein ungültiges UTF-8-Zeichen gefunden wird, gibt es nil und die Position des ungültigen Zeichens zurück. Beachten Sie, dass ungültige UTF-8-Strings auch in Tabellen als Schlüssel und Werte vorhanden sein können.
- Unendliche/NaN-Zahlenexploit - Ausnutzer können Werte wie -1/0, 0/0 oder math.huge senden, um unendliche Währung zu verursachen oder Berechnungen zu brechen. Validieren Sie immer mit den Methoden, die in der Gefahr von NaN beschrieben sind.
Rennbedingungen können auftreten, wenn Spieler die Zeit zwischen Operationen manipulieren, um Gegenstände zu duplizieren oder Daten zu beschädigen.
Handels-Exploit-Beispiel: Ein Spieler initiiert einen Handel, sendet sein Item an einen anderen Spieler und verlässt dann sofort das Spiel. Wenn der Handel abgeschlossen ist, aber ihr DataStore-Speicher aufgrund ungültiger Daten fehlschlägt, treten sie mit ihren ursprünglichen Gegenständen wieder ein, während der andere Spieler die getauschten Gegenstände behält - was zu einer Duplizierung führt.
Präventionsstrategien:
- Validieren Sie alle Daten vor allen Handelsoperationen
- Verwenden Sie transaktionsähnliche Muster, bei denen die Daten aller Spieler validiert werden, bevor Änderungen vorgenommen werden
- Implementieren Sie eine ordnungsgemäße Fehlerbehandlung, die alle Änderungen zurücksetzt, wenn ein Teil fehlschlägt
MarketplaceService
Für Interaktionen, die MarketplaceService betreffen, wie Pässe oder Entwicklerprodukte, müssen alle Kaufvalidierungen und die Gewährung von Gegenständen auf dem Server erfolgen. Nutzen Sie insbesondere den ProcessReceipt-Callback, um Kaufbelege sicher zu validieren. Vertrauen Sie niemals einem clientseitigen Signal, wie PromptProductPurchaseFinished, um einen Kauf ohne Serververifizierung zu bestätigen, da diese gefälscht werden können. Stellen Sie sicher, dass Ihre ProcessReceipt-Funktion die Belegdetails gründlich überprüft und nur nach Bestätigung einer legitimen Transaktion mit den Roblox-Servern Gegenstände oder Währung gewährt, und seien Sie darauf vorbereitet, Fälle zu behandeln, in denen ein Produkt für einen bestimmten Beleg bereits gewährt wurde.