Taktyki bezpieczeństwa i łagodzenie oszustw

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

Roblox wykorzystuje rozproszony system fizyki w którym klienty mają opiekę nad symulacją fizyczną obiektów w ich kontroli, zwykle postacią gracza i niezakotwionymi obiektami w pobliżu tej postaci.Ponadto, poprzez wykorzystanie oprogramowania strony trzeciej, exploiterzy mogą uruchamiać losowy kod Luau na klientzie, aby manipulować modelem danych klienta i dekompilować i wyświetlać kod uruchamiany na nim.

Ogólnie rzecz biorąc, oznacza to, że doświadczony exploiter może potencjalnie wykonać kod, aby oszukiwać w gra, w tym:

  • Teleportowanie własnej postaci wokół miejsce.
  • Strzelanie niezabezpieczone RemoteEvents lub wzywanie RemoteFunctions, takie jak przyznawanie sobie przedmiotów bez zarabiania ich.
  • Dostosowywanie ich charakteru WalkSpeed tak, aby poruszał się bardzo szybko.

Chociaż możesz wdrożyć ograniczone obrony przed atakami, aby złapać powszechne ataki, zdecydowanie zaleca się wdrożenie bardziej niezawodnych taktyk mitigacji po stronie serwera, ponieważ serwer jest ostateczną władzą dla każdego uruchomionego doświadczenia.

Taktyki obronne projektu

Decyzje projektowe podstawowe mogą służyć jako środki bezpieczeństwa "pierwszego kroku", aby zniechęcić do exploitów.Na przykład, w grze strzelczej, w której gracze zdobywają punkty za zabijanie innych graczy, exploiter może stworzyć grupę botów, które teleportują się do tego samego miejsca, aby mogły być szybko zabijane za punkty.Biorąc pod uwagę ten potencjalny exploit, rozważ dwa podejścia i ich przewidywalny wynik:

PodejściePrzewidywalny wynik
Ścigaj boty, pisząc kod, który próbuje je wykryć.
Zmniejsz lub całkowicie usuń przychody punktów za zabójstwa na nowo powstałych graczy.

Choć projekt obronny oczywiście nie jest doskonałym lub kompletnym rozwiązaniem, może przyczynić się do szerszego podejścia do bezpieczeństwa wraz z mitigacją stron serwera.

Mitigacja po stronie serwera

W możliwie największym stopniu serwer powinien wydać ostateczny werdykt co do tego, co jest "prawdą" i jaki jest obecny stan świata.Klienci mogą oczywiście poprosić serwer o dokonanie zmian lub wykonanie akcji, ale serwer powinien potwierdzić i zatwierdzić każdą z tych zmian/akcji przed replikacją wyników do innych graczy.

Z wyjątkiem niektórych operacji fizycznych zmiany w modelu danych na klientzie nie są replikowane na serwer, więc główna ścieżka ataku jest często za pośrednictwem wydarzeń sieciowych, które zadeklarowałeś za pomocą RemoteEvents i RemoteFunctions.Pamiętaj, że exploiter uruchamiający własny kod na twoim klientzie może zażądać ich za pomocą dowolnych danych, które chce.

Walidacja typu wykonywania zdalnego

Jedna ścieżka ataku jest dla exploitera, aby wywołać RemoteEvents i RemoteFunctions z argumentami niewłaściwego wpisywać.W niektórych scenariuszach może to spowodować, że kod na serwerze słuchającym tych zdalnych urządzeń popełni błąd w sposób korzystny dla exploitera.

Podczas używania zdalnych wydarzeń/funkcji możesz zapobiec takiemu rodzajowi ataku, poprawiając typy przekazanych argumentów na serwerze.Moduł "t" , dostępny tutaj, jest przydatny do sprawdzania typu w ten sposób.Na przykład, założąc, że kod modułu istnieje jako ModuleScript o nazwie t wewnątrz ReplicatedStorage:

Lokalny skrypt w StarterPlayerScripts

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
-- Przekaż kolor i pozycję części podczas wywoływania funkcji
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
Skrypt w ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
local t = require(ReplicatedStorage:WaitForChild("t"))
-- Twórz walidator typu z wyprzedzeniem, aby uniknąć niepotrzebnego obciążenia
local createPartTypeValidator = t.tuple(t.instanceIsA("Player"), t.Color3, t.Vector3)
-- Utwórz nową część z przekazanymi właściwościami
local function createPart(player, partColor, partPosition)
-- Sprawdź typ przekazanych argumentów
if not createPartTypeValidator(player, partColor, partPosition) then
-- Cicho powrót "false", jeśli kontrola typu zawodzi tutaj
-- Podnoszenie błędu bez czasu odnowienia może być nadużywane, aby spowolnić serwer
-- Zamiast tego zapewnij zwroty opinii klienta!
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
-- Wiąż "createPart()" z powrotem do zdalnej funkcji
remoteFunction.OnServerInvoke = createPart

Weryfikacja danych

Innym atakiem, który mogą uruchomić nadzorcy, jest wysłanie technicznie poprawnych typów ale uczynienie ich bardzo dużymi, długimi lub w inny sposób uszkodzonymi.Na przykład, jeśli serwer musi wykonać kosztowną operację na strunie, która skaluje się z długością, haker może wysłać niesamowicie dużą lub niedopasowaną strunę, aby zablokować serwer.

Podobnie, zarówno inf i NaN będą type() jako number , ale oba mogą powodować poważne problemy, jeśli atakujący wysyła je i nie są obsługiwane poprawnie za pomocą funkcji takich jak obserwuje:


local function isNaN(n: number): boolean
-- NaN nigdy nie jest równy sobie
return n ~= n
end
local function isInf(n: number): boolean
-- Liczba może być -inf lub inf
return math.abs(n) == math.huge
end

Kolejny powszechny atak, który mogą wykorzystać nadzorcy, polega na wysłaniu tables w miejsce Instance.Złożone ładowania mogą naśladować to, co byłoby zwykłym odniesieniem do obiektu.

Na przykład, dostarczony z systemem sklepu w doświadczeniu , gdzie dane o przedmiotach, takie jak ceny, są przechowywane w obiektach, exploiter może ominąć wszystkie inne kontrolki, wykonując obserwujedziałania:

Lokalny skrypt w 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, -- Można również używać negatywnych wartości, co skutkuje nadaniem waluty zamiast jej przyjęcia!
},
}
-- Wyślij złośliwy ładunek na serwer (zostanie odrzucony)
print(buyItemEvent:InvokeServer(payload)) -- Wyświetla "nieprawidłowy podany przedmiot"
-- Wyślij prawdziwy przedmiot na serwer (to przejdzie!)
print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Outputs "true" and remaining currency if purchase succeeds
Skrypt w ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- Sprawdź, czy przekazany przedmiot nie jest sfałszowany i znajduje się w katalogu ItemData
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Invalid item provided"
end
-- Serwer może następnie przetworzyć zakup w oparciu o przykładowy przepływ poniżej
end
-- Wiąż "buyItem()" z powrotem zdalnej funkcji
buyItemEvent.OnServerInvoke = buyItem

Weryfikacja wartości

Oprócz weryfikacji typów i danych , powinieneś weryfikować wartości przekazywane za pośrednictwem i , zapewniając, że są ważne i logiczne w kontekście żądanym.Dwa powszechne przykłady to sklep w doświadczeniu i system celowania w broń .

Sklep w robić zakupy

Rozważ system sklepu w doświadczeniu z interfejsem użytkownika, na przykład menu wyboru produktu z przyciskiem "Kup".Gdy naciśniesz przycisk, możesz zażądać RemoteFunction pomiędzy klientem a serwerem, aby poprosić o kupować.Jednak ważne jest, aby serwer potwierdził , najbardziej niezawodny menadżer doświadczenia, że użytkownik ma wystarczająco pieniędzy, aby kupić przedmiot.

Example purchase flow from client to server through a RemoteEvent
Przykładowy przepływ zakupów od klienta do serwera za pośrednictwem RemoteFunction

Celowanie bronią

Scenariusze walki wymagają szczególnej uwagi w zakresie weryfikacji wartości, w szczególności poprzez celowanie i weryfikację trafień.

Wyobraź sobie grę, w której gracz może strzelić laserową wiązkę do innego gracza.Zamiast klient powiedzieć serwerowi komu zaszkodzić, powinien zamiast tego powiedzieć serwerowi pozycję pochodzenia strzału i część/pozycję, które uważa, że uderzył.Serwer może następnie zweryfikować obserwuje:

  • Pozycja, z której klient raportuje strzelanie z jest blisko postaci gracza na serwerze.Zauważ, że serwer i klient będą się nieco różnić ze względu na opóźnienie, więc konieczna będzie zastosowanie dodatkowej tolerancji.

  • Pozycja, o której klient raportuje uderzenie , jest rozsądnie bliska pozycji części , której klient raportuje uderzenie, na serwerze.

  • Nie ma statycznych przeszkód między pozycją, z której klient raportuje strzelanie, a pozycją, do której klient raportuje strzelanie.Ta kontrola zapewnia, że klient nie próbuje strzelać przez ściany.Zauważ, że powinno to sprawdzać tylko statyczną geometrię, aby uniknąć odrzucenia ważnych strzałów z powodu opóźnienia. Dodatkowo , możesz chcieć wdrożyć dalsze weryfikacje strony serwera w następujący sposób:

  • Śledź, kiedy gracz ostatnio strzelił swoją bronią i zweryfikuj, czy nie strzela zbyt szybko.

  • Śledź ilość amunicji każdego gracza na serwerze i potwierdź, że strzelający gracz ma wystarczającą ilość amunicji, aby wykonać atak bronią.

  • Jeśli wdrożyłeś frakcje lub system walki "gracze przeciwko botom", potwierdź, że uderzony charakter jest wróg, przeciwnik, a nie sojusznikiem.

  • Potwierdź, że trafiony gracz jest żywy.

  • Przechowuj stan broń i gracza na serwerze i potwierdź, że strzelający gracz nie jest blokowany przez obecną akcję, taką jak ponowne ładowanie lub stan takich jak sprint.

Manipulacja przechowaniem danych

W doświadczeniach korzystających z DataStoreService do zapisywania danych gracza, nadzorcy mogą skorzystać z nieprawidłowych danych i bardziej niejasnych metod, aby zapobiec zapisywaniu się DataStore prawidłowo.Może to być szczególnie nadużywane w doświadczeniach z handlem przedmiotami, rynkami i podobnymi systemami, w których przedmioty lub waluta opuszczają wyposażeniegracza.

Upewnij się, że wszelkie działania wykonywane za pośrednictwem RemoteEvent lub RemoteFunction, które wpływają na dane gracza za pomocą wejścia klienta, są oczyszczone na podstawie obserwuje:

  • Instance wartości nie mogą zostać zakodowane w DataStore i nie powiodą się. Skorzystaj z walidacji typu , aby zapobiec temu.
  • DataStores mają ograniczenia danych.Paski o dowolnej długości należy sprawdzić i/lub ograniczyć, aby uniknąć tego, zapewniając, że nieograniczone losowe klucze nie mogą być dodawane do tabel przez klienta.
  • Indeksy tabel nie mogą być NaN lub nil. Powtarzaj wszystkie tabele przekazane przez klienta i sprawdź, czy wszystkie indeksy są ważne.
  • DataStores może akceptować tylko ważne znaki UTF-8, więc powinieneś oczyścić wszystkie ciągi dostarczone przez klienta za pomocą utf8.len() , aby upewnić się, że są ważne.utf8.len() zwróci długość ciągu, traktując znaki unicode jako pojedynczą literę; jeśli spotkano nieprawidłowy znak UTF-8, zwróci nil i pozycję nieprawidłowego znaku.Zauważ, że nieprawidłowe struny UTF-8 mogą również występować w tabelach jako klucze i wartości.

Ograniczanie zdalne

Jeśli klient jest w stanie ukończyć operację kosztowną pod względem obliczeń na serwerze lub uzyskać ograniczoną usługę z szybkością, taką jak DataStoreService poprzez RemoteEvent, ważne jest, aby wdrożyć ograniczenie częstotliwości , aby zagwarantować, że operacja nie jest zbyt często wywoływana.Ograniczenie szybkości można wdrożyć poprzez śledzenie, kiedy klient po raz ostatni zawołał zdalne wydarzenie i odrzucenie następnej prośby, jeśli zostanie wezwane zbyt wcześnie.

Weryfikacja ruchu

W przypadku doświadczeń konkurencyjnych możesz chcieć zweryfikować ruchy postaci gracza na serwerze, aby upewnić się, że nie teleportują się po mapie lub nie poruszają się szybciej niż akceptowane.

  1. W odstępach 1 sekundy sprawdź nową lokalizację postaci w porównaniu z wcześniej zapisaną lokalizacją.

    Image showing moving character's position on a straight path in increments of 1 second
  2. Określ maksymalną "dopuszczalną" zmianę w odległości w oparciu o WalkSpeed (study per second) znaków, pomnożoną przez ~1.4, aby uwzględnić pewną tolerancję wobec opóźnienia serwera.Na przykład, przy domyślnym WalkSpeed 16, tolerowalna delta wynosi ~22.

    Image showing tolerable change in distance based on character's walk speed
  3. Porównaj rzeczywistą różnicę dystansu z tolerowaną różnicą i postępuj w następujący sposób:

    • Dla tolerowalnego delta, zapisz nową lokalizację znaku w przygotowaniu do następnego zwiększonego sprawdzać.
    • W przypadku nieoczekiwanej lub nie do zniesienia delta (potencjalny exploit prędkości/teleportacji):
      1. Zwiększ odrębną wartość "liczby przestępstw" dla gracza, w porównaniu z karą za "fałszywy pozytywny" wynikujący z ekstremalnego opóźnienia serwera lub innych czynników niezwiązanych z exploitem.
      2. Jeśli wystąpi duża liczba wykroczeń w ciągu 30-60 sekund, Kick() całkowicie odnowić gracza z doświadczenia; w przeciwnym razie zresetuj liczbę "liczby wykroczeń".Zauważ, że kiedy wyrzucasz gracza za oszustwo, najlepszą praktyką jest rejestracja wydarzenia, abyś mógł śledzić, ilu graczy zostało dotkniętych.