Bir istemcinin sunucuda bir eylemi tetikleyebilmesi durumunda, kötüye kullanım potansiyeli vardır. RemoteEvents ve RemoteFunctions burada en yaygın saldırı vektörleri olsa da, ProximityPrompt gibi diğer durumlar da hassastır. Bu bölüm, bu sınırı güvence altına almanın yollarını ele alır.
Uzaktan girişleri doğrulama
Bir istemciden gönderilen her veri parçası, kullanılmadan önce sunucu tarafından doğrulanmalıdır. Uzaktan ne yaptığınıza bağlı olarak bu doğrulama katmanlarını uygulayın.
Bağlam/izin doğrulaması
Bu, oyunun durumunu, ilerlemesini veya diğer oyuncuları etkileyen uzantılar için gereklidir. Sunucu, oyuncunun isteği yapmak için gerekli izinlere sahip olup olmadığını doğrulamalıdır. Örneğin, oyuncu bir şey satın almak için bir dükkana yeterince yakın mı? Kapıyı açmak için gerekli bir anahtara sahip mi? Karakteri hayatta mı?
Doğrulama, aşağıdaki durumlar için gereklidir:
- Ödüller veren veya oyuncu ilerlemesini değiştiren
- Diğer oyuncuları veya paylaşılan oyun durumunu etkileyen
- Mesafe, zamanlama veya izin gereksinimleri olan eylemleri gerçekleştiren (örneğin, dükkân mesafesi)
Tür ve yapı doğrulaması
Kötü niyetli kişiler tarafından başlatılabilecek bir diğer saldırı, teknik olarak geçerli türler göndermek ancak bunları son derece büyük, uzun veya başka şekilde bozuk hale getirmektir. Örneğin, sunucu uzunluğa göre ölçeklenen bir dize üzerinde pahalı bir işlem yapmak zorunda kalırsa, bir kötü niyetli kişi sunucu yavaşlatmak için son derece büyük veya bozuk bir dize gönderebilir.
Kötü niyetli kişilerin kullanabileceği bir diğer yaygın saldırı, tables göndermektir. Karmaşık yükler, aksi takdirde sıradan bir nesne referansını taklit edebilir.
Örneğin, fiyatlar gibi öğe verilerinin NumberValue nesnelerinde saklandığı bir oyun içi dükkân sistemi sağlandığında, bir kötü niyetli kişi aşağıdakileri yaparak tüm diğer kontrolleri atlayabilir:
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, -- Negatif değerler de kullanılabilir, bu da para vermek yerine almakla sonuçlanır!
},
}
-- Kötü niyetli yükü sunucuya gönder (bu reddedilecektir)
print(buyItemEvent:InvokeServer(payload)) -- "false Invalid item provided" çıktısını verir
-- Gerçek bir öğeyi sunucuya gönder (bu geçecektir!)
print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- "true" çıktısını verir ve satın alma başarılı olursa kalan parayı gösterirlocal ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- Geçerli öğenin sahte olmadığını ve ItemData klasöründe olduğunu kontrol et
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Geçersiz öğe sağlandı"
end
-- Sunucu, aşağıdaki örnek akışa göre satın almayı işleyebilir
end
-- "buyItem()" fonksiyonunu uzaktan fonksiyonun geri çağrısına bağla
buyItemEvent.OnServerInvoke = buyItemDeğer doğrulaması
Türleri ve verileri doğrulamanın yanı sıra, RemoteEvents ve RemoteFunctions aracılığıyla geçen değerleri de doğrulamalısınız; bunların geçerli ve mantıklı olduğundan emin olun. Bu, para, öğeler, sayısal girişler veya kullanıcı tarafından oluşturulan içerik ile ilgilenen uzantılar için gereklidir. Değerlerin beklenen sınırlar içinde olduğundan emin olmalısınız; örneğin, satın alma miktarının sıfırdan büyük olması veya bir öğe kimliğinin oyununuzdaki gerçek bir öğeye karşılık gelmesi gibi.
inf ve NaN'nin geçerli sayı türleri olduğunu unutmayın, ancak her ikisi de bir kötü niyetli kişi tarafından gönderildiğinde ve doğru bir şekilde işlenmediğinde büyük sorunlara yol açabilir. Bunları tespit etmek ve reddetmek için math.isfinite() kullanın:
-- NaN veya +/-inf olan sayısal değerleri reddet
if not math.isfinite(offeredGold) then
return false, "Geçersiz sayı sağlandı"
endNaN tehlikesi
Bir kötü niyetli kişi, bir argüman olarak NaN (Not a Number) gönderebilir. NaN, "sayı" türünde olduğu için benzersiz bir tehlike taşır, ancak tüm standart karşılaştırmalarda başarısız olur; bu da mantıksal kontrolleri geçmek için ince bir şekilde atlatmasına olanak tanır. Bir oyuncunun bir teklif yaptığı savunmasız bir ticaret sistemine bakalım.
-- SAVUNMASIZ SUNUCU KODU
local function onCreateTradeOffer(player, offeredGold)
-- 1. TÜR KONTROLÜ: Bu geçer! typeof(NaN) "number"dır.
if typeof(offeredGold) ~= "number" then
return "Geçersiz teklif"
end
-- 2. ARALIK KONTROLÜ: Bu atlanır!
-- (NaN < 0) yanlıştır. (NaN > 1000000) de yanlıştır. Kontrol hiçbir şey yapmaz.
if offeredGold < 0 or offeredGold > 1000000 then
return "Teklif aralığın dışında"
end
-- 3. ENVENTAR KONTROLÜ: Bu atlanır!
-- (NaN > player.Gold.Value) yanlıştır.
if offeredGold > player.Gold.Value then
return "Yeterli altın yok"
end
-- SAVUNMASIZLIK: NaN altın ile sahte bir ticaret teklifi oluşturuldu!
createTrade(player, {gold = offeredGold})
return "Ticaret teklifi oluşturuldu."
endKötü niyetli kişi, hiçbir altın olmadan bir ticaret teklifi oluşturmayı başardı, çünkü her kontrol sessizce başarısız oldu. Dahası, bu tek NaN değeri, onu kullanmaya çalışan diğer sistemleri zehirleyecek ve teklif aracılığıyla diğer oyunculara yayılacaktır.
Aşağıda, yaygın oyun içi özelliklerin güvenli tasarımını kapsayan bazı yüksek düzeyde örnekler bulunmaktadır.
Oyun içi dükkân
Bir kullanıcı arayüzüne sahip bir oyun içi dükkân sistemi düşünün; örneğin, "Satın Al" düğmesi olan bir ürün seçim menüsü. Düğmeye basıldığında, bir RemoteFunction aracılığıyla istemci ile sunucu arasında satın alma talebinde bulunabilirsiniz. Ancak, oyunun en güvenilir yöneticisi olan sunucunun, kullanıcının öğeyi satın almak için yeterli paraya sahip olduğunu doğrulaması önemlidir.

Silah hedefleme
Savaş senaryoları, değerlerin doğrulanmasına özel dikkat gerektirir, özellikle nişan alma ve vurma doğrulaması yoluyla.
Bir oyuncunun başka bir oyuncuya lazer ışını ateşleyebildiği bir oyunu hayal edin. İstemcinin sunucuya kime zarar vereceğini söylemesi yerine, sunucuya atışın başlangıç konumunu ve vurduğunu düşündüğü parça/konumu bildirmelidir. Sunucu daha sonra aşağıdakileri doğrulayabilir:
- İstemcinin bildirdiği atış konumu, sunucudaki oyuncunun karakterine yakın olmalıdır. Sunucu ve istemci, gecikme nedeniyle biraz farklılık göstereceğinden, ek bir tolerans uygulanması gerekecektir.
- İstemcinin bildirdiği vurma konumu, istemcinin vurduğunu bildirdiği parçanın konumuna makul bir şekilde yakın olmalıdır.
- İstemcinin bildirdiği atış konumu ile istemcinin bildirdiği vurma konumu arasında statik engeller olmamalıdır. Bu kontrol, bir istemcinin duvarlardan geçmeye çalışmadığını garanti eder. Bu, geçerli atışların gecikme nedeniyle reddedilmesini önlemek için yalnızca statik geometrinin kontrol edilmesi gerektiğini unutmayın.
Ayrıca, sunucu tarafında aşağıdaki gibi ek doğrulamalar uygulamak isteyebilirsiniz:
- Oyuncunun en son ne zaman silah ateşlediğini takip edin ve çok hızlı ateş etmediğinden emin olun.
- Her oyuncunun mermi miktarını sunucuda takip edin ve ateş eden oyuncunun silah saldırısını gerçekleştirmek için yeterli mermisi olduğundan emin olun.
- Takımlar veya "oyunculara karşı botlar" savaş sistemi uyguladıysanız, vurulan karakterin bir düşman olduğunu, takım arkadaşı olmadığını doğrulayın.
- Vurulan oyuncunun hayatta olduğunu doğrulayın.
- Silah ve oyuncu durumunu sunucuda saklayın ve ateş eden oyuncunun yeniden yükleme gibi mevcut bir eylem veya koşul (örneğin, koşma) tarafından engellenmediğini doğrulayın.
Hız sınırlama
Sunucu tarafı mantığının istemci tarafından tetiklenebileceği her durumda (uzaktan, Touched olayları, yakınlık istemleri, ClickDetectors vb.), bu mantığın kötü niyetli kişiler veya hatta meşru kullanıcılar tarafından spam yapılma olasılığı vardır. Hız sınırlama, bu eylemlerin ne sıklıkla gerçekleştirilebileceğini kontrol eder, kötüye kullanımı ve sistem aşırı yüklenmesini önler. İstemci tarafında hız sınırlamaları uygulamak pratik (ve bazen gereklidir), ancak asla yalnızca istemci tarafında bir hız sınırlamasına güvenmeyin.
İstemci tarafından tetiklenebilen herhangi bir sunucu tarafı mantığı için, her zaman bu mantığın çalışması gereken maksimum hızı düşünün. Birçok sunucu tarafı eyleminin, kötüye kullanımı veya başarısızlığı önlemek için sıklığının sınırlanması gerekir, örneğin:
- Roblox API çağrıları: DataStoreService ve BadgeService gibi arka uç API'leri, aşılması durumunda isteklerinizin başarısız olmasına neden olacak yerleşik hız sınırlamalarına sahiptir.
- Hesaplama açısından pahalı işlemler: Önemli sunucu kaynaklarını tüketen veya diğer istemcileri etkileyen eylemler
- Sunucu örneği: ServerStorage'dan büyük modellerin kopyalanması, bunlar asla istemcilere çoğaltılmasa bile
- İstemci örneği: Tüm bağlı istemcilere aynı anda GUI'lerini güncellemeleri talimatı vermek
- Kötüye kullanılabilir mekanikler: Hızlı bir şekilde gerçekleştirildiğinde kötüye kullanılabilecek eylemler, örneğin, dokunulmazlık verme, oyuncuları teleport etme veya para verme
Token bucket örneği
Hız sınırlama için sağlam ve yaygın bir yaklaşım token bucket algoritmasıdır. Her kullanıcının belirli sayıda token tutabilen bir kovası olduğunu hayal edin. Bir eylemi gerçekleştirmek için kullanıcı bir token harcamalıdır. Kova, yeni tokenlerle sabit bir hızda yeniden dolmaktadır. Bu yöntem, gerektiğinde kısa patlamalara izin verir, ancak zaman içinde ortalama bir hız uygulayarak sürekli spam yapılmasını önler.
--!strict
-- ServerScriptService'de bulunan modül
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
-- En fazla `capacity` olayına izin veren bir sınırlayıcı oluşturur, `capacity / windowSeconds` oranında yeniden dolarak
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
-- Kullanıcının limitini aşması durumunda false döner
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 TokenBucketModülü kullanmak için önce TokenBucket.new(capacity, windowSeconds) ile yeni bir sınırlayıcı örneği oluşturursunuz. capacity, bir kullanıcının hızlı bir patlama içinde yapabileceği maksimum istek sayısıdır ve windowSeconds, tüm bu tokenlerin yeniden dolması için geçen süredir. Örneğin, TokenBucket.new(5, 10) 5 isteğe kadar patlamalara izin veren ve her iki saniyede bir token yenileyen bir sınırlayıcı oluşturur (10 saniye / 5 token).
Korunan mantığı yürütmeden önce, :allow(userId) yöntemini çağırın; false dönerse eylemi reddedin. Ayrıca, kullanıcı ayrıldığında kullanıcıya ait kova verilerini temizlemek iyi bir uygulamadır.
Aşağıdaki örnek script, bir RemoteEvent'in oyuncular tarafından spam yapılmasını önlemek için nasıl korunduğunu göstermektedir.
-- Sunucu scriptinde örnek kullanım
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
-- 10 saniyede 5 mesaj (kapasite 5, 0.5 token/saniye hızında yeniden doluyor)
local chatLimiter = TokenBucket.new(5, 10)
ChatRemote.OnServerEvent:Connect(function(player: Player, message: string)
if not chatLimiter:allow(player.UserId) then
-- Çok hızlı: isteği düşür veya spam uyarısı gönder
return
end
-- Mesajı işleyin (diğer doğrulamalardan sonra)
broadcastMessage(player, message)
end)
-- Bellek sızıntılarını önlemek için temizleme
Players.PlayerRemoving:Connect(function(p: Player)
chatLimiter.buckets[p.UserId] = nil
end)İstemci tarafından tetiklenen nesneleri güvence altına alma
ProximityPrompts, ClickDetectors ve DragDetectors uzaktan değildir, ancak bir kötü niyetli kişi bu olayları herhangi bir mesafeden, herhangi bir zamanda tetikleyebilir ve genellikle Enabled veya MaxActivationDistance gibi özellikleri göz ardı edebilir. Bu nesneler, uzaktan olduğu kadar titizlikle güvence altına alınmalıdır.
ProximityPrompt
- Bir kötü niyetli kişi, sunucuda Enabled false olsa bile herhangi bir olayı ateşleyebilir.
- Enabled, MaxActivationDistance ve RequiresLineOfSight istemcide sessizce değiştirilebilir, bu nedenle herhangi bir çoğaltılmış ProximityPrompt her yerden görülebilir ve potansiyel olarak etkileşimde bulunulabilir.
- Sunucu, HoldDuration sunucu tarafında sıfır olsa bile tutma olaylarını kabul eder. İstemci, etkileşimleri anormal bir şekilde hızlı gerçekleştirmek için HoldDuration'ı manipüle edebilir.
- Sadece Triggered olayı sunucu tarafında bir mesafe kontrolüne sahiptir. Diğer olaylar (örneğin, PromptButtonHoldBegan ve TriggerEnded) mesafe kontrolüne sahip değildir ve herhangi bir istemci tarafından keyfi olarak gönderilebilir.
ClickDetector
- Sunucuda herhangi bir olay için hiç kontrol yoktur.
- Kötü niyetli kişiler, ClickDetector devre dışı olsa bile (0'lık MaxActivationDistance veya DragDetector.Enabled false) herhangi bir mesafede herhangi bir olayı çoğaltabilir.
- Olaylar, ClickDetector bir tıklanabilir şeye bağlı olmasa bile çoğaltılabilir.
DragDetector
- ClickDetector'dan miras alınan olaylar için hiç kontrol yoktur.
- Sürükleme olayları için Enabled özelliği ve PermissionPolicy özellikleri sunucu tarafından kontrol edilir ve saygı gösterilir. Diğer tüm özellikler kontrol edilmez.
Bu olaylardan biri tetiklendiğinde, sunucu scriptiniz herhangi bir eylemi gerçekleştirmeden önce kendi doğrulamasını yapmalıdır:
- Etkin olup olmadığını kontrol edin: Nesnenin özelliklerini (örneğin, Enabled, HoldDuration, RunLocally vb.) sunucuda kontrol ederek etkin olmasının amaçlandığından emin olun. Bu adım, nesne her zaman etkinse gerekli değildir.
- Oyuncu durumunu kontrol edin: Oyuncunun mevcut durumu göz önüne alındığında eylemi gerçekleştirebilir olduğundan emin olun. Oyuncunun karakteri dünyada mı? Karakteri nesneye yeterince yakın mı? Bu nesneyle etkileşimde bulunmak için oyuncunun hayatta olması gerekir mi? Gerekirse sunucuda oyununuz tarafından tutulan diğer oyuncu durumlarını kontrol edin.
- Hız sınırlama uygulayın: Hız Sınırlama bölümünde ele alındığı gibi, istemcide soğuma süreleri uygulayın ve bu olayların spam yapılmasını ve kötüye kullanılmasını önlemek için sunucuda hız sınırlamaları uygulayın. Minimum süre alması beklenen etkileşimler için, istemcilerin bunları çok hızlı tamamlamadığından emin olun.
Ağ sahipliği hakkında not: Varsayılan olarak, bu nesnelerin herhangi biri, sabit olmayan parçaların veya sabit olmayan bir montajın çocuklarıysa, bir kötü niyetli kişi ana parçaların ağ sahipliğini alabilir ve bunları doğrudan karakterine taşıyabilir, mesafe kontrollerini atlayabilir. Kritik eylemler için, sabit parçalar veya ağ sahipliği API'sini kullanın ve gerekli sunucu tarafı doğrulamasını uygulayın.
RemoteEvents ve RemoteFunctions
RemoteFunctions ve RemoteEvents'in kapsamını ve etkisini sınırlayın. Uzaktan fonksiyonlar/olayların argümanlarına dayalı olarak herhangi bir varlığı (hatta dokular veya sesler) dinamik olarak yüklerken veya oyun kodunu (özellikle require aracılığıyla) çalıştırırken son derece dikkatli olun. Sunucunun silmesi veya değiştirmesi için istemcinin keyfi bir yol veya nesne referansı belirtmesine izin veren uzaktan fonksiyonlar uygulamaktan kaçının; bu tür değişiklikler önemsiz görünse bile. Keyfi nesne durumunu değiştirebilen veya DataModel ağacında yaygın değişiklikler yapabilen uzaktan fonksiyonlar, genellikle diğer hatalarla zincirlenerek genel durumu veya mantığı ciddi şekilde etkileyebilir. Sadece nesne argümanlarının türünü değil, aynı zamanda sınıfını ve DataModel içindeki beklenen konumunu veya yapısını da kontrol edin.
İstemciden istemciye iletişim
Bazı uzaktan fonksiyonlar, bir istemcinin diğer istemcilerde etkiler tetiklemesine izin vermek için tasarlanmıştır. Bu, bir istemcinin sunucuya bir uzaktan fonksiyon göndermesi ve ardından sunucunun RemoteEvent:FireAllClients(), RemoteEvent:FireClient() veya benzeri yöntemleri kullanarak bilgiyi iletmesi durumunda gerçekleşir. Bu desen, düzgün bir şekilde güvence altına alınmadığında tehlikeli olabilir - sunucu bir kapı bekçisi olmalıdır, sadece bir iletici değil.
Oyuncuların bir yıldırım büyüsü atabildiği bir senaryoyu hayal edin. Atıldığında, yakınlardaki oyuncular parlamayı görür, sesi duyar ve kamera sarsıntısı yaşar.
Savunmasız bir sunucu scripti şöyle görünebilir:
local castLightningEvent = game.ReplicatedStorage.CastLightning
-- Bu sunucu scripti, hiçbir doğrulama olmadan mesajı herkese iletir
castLightningEvent.OnServerEvent:Connect(function(player, strikePosition)
castLightningEvent:FireAllClients(strikePosition)
end)local castLightningEvent = game.ReplicatedStorage.CastLightning
-- İstemcide görsel ve ses efektleri oluştur
local function createLightningEffect(strikePosition)
-- Kamerayı sarsma kodu
-- Yüksek sesli bir yıldırım sesi çalma kodu
-- Görsel bir yıldırım çarpması oluşturma kodu
print("Yıldırım şu noktada çarptı: " .. tostring(strikePosition))
end
-- Sunucu olayı yayınladığında, bu istemci etkiyi çalıştırır
castLightningEvent.OnClientEvent:Connect(createLightningEffect)Bir kötü niyetli kişi, bu savunmasız sistemi kötüye kullanarak:
- Spam yapma: Uzaktan fonksiyonu saniyede yüzlerce kez çağırarak, oyunu oynanamaz hale getiren sürekli etkiler oluşturma
- Geçersiz veri: Tüm istemcilerde script hatalarına neden olan nil, NaN veya yanlış türler gönderme
- Yetkisiz kullanım: Açmadıkları veya kaynakları olmayan büyüleri atma
Sunucu, bir kapı bekçisi olmalıdır, sadece bir iletici değil. Yayınlamadan önce, isteği doğrulamalı ve oyuncu başına hız sınırlaması uygulamalıdır. İşte isteği doğrulamak için kullanılabilecek bazı yöntemlerin örneği.
local castLightningEvent = game.ReplicatedStorage.CastLightning
local playerCooldowns = {}
local COOLDOWN_TIME = 3
castLightningEvent.OnServerEvent:Connect(function(player, strikePosition)
-- 1. Tür doğrulaması
if typeof(strikePosition) ~= "Vector3" then
return -- Geçersiz tür, sessizce reddet
end
-- 2. NaN kontrolü (NaN ~= NaN, onu tespit etmenin tek yoludur)
if strikePosition.X ~= strikePosition.X then
return -- NaN içeriyor, reddet
end
-- 3. Örnek Hız sınırlama
local lastCast = playerCooldowns[player] or 0
if tick() - lastCast < COOLDOWN_TIME then
return -- Hala soğuma süresinde
end
-- 4. Örnek İzin kontrolü
if not player:GetAttribute("HasLightningSpell") then
return -- Oyuncunun bu büyüsü yok
end
-- 5. Örnek Mesafe doğrulaması
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 -- Mesafe dışı
end
-- Tüm kontroller geçti - yayınlamak için güvenli
playerCooldowns[player] = tick()
castLightningEvent:FireAllClients(player, strikePosition)
end)Bu kontrolleri uygulayarak, bir kötü niyetli kişinin diğer oyuncular için oyunu mahvetmesini zorlaştırmış olursunuz. Sunucu, yalnızca geçerli, yetkilendirilmiş eylemlerin diğer istemcilere yayınlanmasını sağlayan bir koruyucu bariyer görevi görür.
Özel dikkate alınması gerekenler
Aşağıdakiler, özel işleme gerektirebilecek bazı ek durumları göz önünde bulundurmalısınız.
Veri deposu manipülasyonu
DataStoreService kullanarak oyuncu verilerini kaydeden oyunlarda, kötü niyetli kişiler geçersiz veriler, yarış koşulları kullanarak kayıtları bozabilir veya bir DataStore'da öğeleri çoğaltabilir. Bu, özellikle öğe ticareti, pazar yerleri ve para sistemleri olan oyunlarda sorunludur.
Bir RemoteEvent veya RemoteFunction aracılığıyla istemci girişi ile oyuncu verilerini etkileyen herhangi bir eylemin, aşağıdakilere dayalı olarak temizlendiğinden emin olun:
- Nesne değerleri bir DataStore'a serileştirilemez ve başarısız olur. Bunu önlemek için tür doğrulaması kullanın.
- DataStore'ların veri sınırları vardır. Keyfi uzunluktaki dizeler kontrol edilmeli ve/veya sınırlandırılmalıdır; ayrıca istemcinin tablolara sınırsız keyfi anahtarlar eklemesine izin verilmemelidir.
- Tablo indeksleri NaN veya nil olamaz. İstemci tarafından geçirilen tüm tablolarda yineleyin ve tüm indekslerin geçerli olduğunu doğrulayın.
- DataStore'lar yalnızca geçerli UTF-8 karakterlerini kabul eder, bu nedenle istemci tarafından sağlanan tüm dizeleri utf8.len() ile temizlemelisiniz; geçerli olup olmadıklarını kontrol etmek için. utf8.len(), bir dizenin uzunluğunu döndürür ve unicode karakterlerini tek bir karakter olarak ele alır; geçersiz bir UTF-8 karakteri ile karşılaşılırsa nil ve geçersiz karakterin konumunu döndürür. Geçersiz UTF-8 dizeleri, anahtarlar ve değerler olarak tablolarda da bulunabilir.
- Sonsuz/NaN sayı istismarları - Kötü niyetli kişiler, sonsuz para veya hesaplamaları bozmak için -1/0, 0/0 veya math.huge gibi değerler gönderebilir. Her zaman NaN tehlikesi bölümünde açıklanan yöntemleri kullanarak doğrulama yapın.
Yarış koşulları, oyuncuların işlemler arasındaki zamanlamayı manipüle ederek öğeleri çoğaltmalarına veya verileri bozmalarına neden olabilir.
Ticaret istismar örneği: Bir oyuncu bir ticareti başlatır, öğesini başka bir oyuncuya gönderir ve hemen oyundan çıkar. Ticaret tamamlanırsa ancak DataStore kaydı geçersiz veriler nedeniyle başarısız olursa, orijinal öğeleriyle tekrar katılırken diğer oyuncu ticaret edilen öğeleri tutar - bu da çoğaltmaya neden olur.
Önleme stratejileri:
- Tüm verileri herhangi bir ticaret işlemi öncesinde doğrulayın
- Tüm oyuncuların verilerinin doğrulandığı işlem benzeri kalıplar kullanın ve herhangi bir değişiklik yapmadan önce tüm değişiklikleri onaylayın
- Herhangi bir kısım başarısız olursa tüm değişiklikleri geri alacak uygun hata işleme uygulayın
MarketplaceService
MarketplaceService ile ilgili etkileşimlerde, örneğin geçişler veya geliştirici ürünleri gibi, tüm satın alma doğrulaması ve öğe verme sunucuda gerçekleşmelidir. Özellikle, satın alma makbuzlarını güvenli bir şekilde doğrulamak için ProcessReceipt geri çağrısını kullanın. PromptProductPurchaseFinished gibi istemci tarafı sinyallerine güvenmeyin; bu sinyaller sahte olabilir. ProcessReceipt fonksiyonunuzun makbuz detaylarını kapsamlı bir şekilde kontrol ettiğinden ve yalnızca Roblox sunucularıyla meşru bir işlem onaylandıktan sonra öğeleri veya parayı verdiğinden emin olun ve belirli bir makbuz için bir ürünün zaten verilmiş olabileceği durumları ele almaya hazır olun.