Güvenlik
Bu sayfa, Open Play ile entegre bir oyunu güvenli işletmek için bilmeniz gerekenleri toplar: anahtarları nerede tutacağınız, nasıl döndüreceğiniz, tarayıcı oturumunun nasıl korunduğu ve webhook isteklerini nasıl doğrulayacağınız. Üretime çıkmadan önce üretim kontrol listesi ile birlikte okuyun.
1. API anahtarları
İki tür anahtar vardır ve ikisi de tek bir ortama bağlıdır:
| Tür | Nerede tutulur | Ne yapabilir |
|---|---|---|
Sunucu anahtarı (server) | Yalnız sunucunuzda (gizli yönetim aracı ya da ortam değişkeni) | Tüm sunucu uçları |
İstemci anahtarı (client) | İstemciye gömülebilir | Hassas komutlar (ör. add_xp, set_attribute) çalışmaz; ama bazı uçlarda anahtar türü kapısı yoktur (uç noktalar) |
Uyarı
İstemci anahtarı “zararsız” değildir. Karakter oluşturma/silme, envanter hareketleri, takas, tüccar alım-satımı ve cüzdan aktarımı gibi uçlarda kodda anahtar türü kapısı yoktur; istemciye gömülen anahtar bunları çağırabilir. Bu uçları yalnız sunucunuzdan çağırın, oyuncu için ise oyuncu oturumuyla çalışan uçları (
/v1/player/..., eklentiler,PlayerClient) kullanın.
Anahtar biçimi ve saklama
- Biçim:
crx_live_<önek>_<gizli>. Sunucu yalnız anahtarın özetini saklar (argon2id); gizli kısmı geri üretemez. - Gizli değer yalnız oluşturma anında bir kez gösterilir. Kaybederseniz geri alınamaz; yeni anahtar üretirsiniz.
- Anahtarı depoya, günlüğe, hata raporuna, istemci paketine, ekran görüntüsüne koymayın.
Authorizationbaşlığını günlüklerden süzün. - Ortam başına ve hizmet başına ayrı anahtar kullanın (ör. canlı API sunucusu, toplu iş, yönetim betiği); bir sızıntıda yalnız birini iptal edersiniz.
- Anahtara son kullanma tarihi verin. Panelde oluştururken
expiresAtayarlanabilir; süresi dolan anahtar reddedilir.
Yaşam döngüsü
Anahtarlar panelde Oyun ayarları: API anahtarları ekranında yönetilir (API anahtarları):
| Eylem | Etki |
|---|---|
| Oluştur | Yeni anahtar; gizli değer bir kez gösterilir |
| Devre dışı bırak / etkinleştir | Geçici ve geri alınabilir |
| Döndür (rotate) | Aynı adla yeni anahtar üretir ve eskisini aynı işlemde hemen iptal eder |
| İptal et (revoke) | Kalıcı; geri alınamaz |
| Sil | Yumuşak silme + iptal |
Anahtar durumu her istekte veritabanından yeniden okunur: iptal ya da devre dışı bırakma bir sonraki istekte etkili olur (yalnız gizli değerin doğrulama sonucu 60 sn önbelleğe alınır, durum alınmaz). Her işlem için gerekçe yazılır ve işlem geçmişine düşer.
Kesintisiz döndürme
Paneldeki Döndür eylemi eski anahtarı hemen iptal eder; yeni anahtarı dağıtana kadar çağrılarınız 401 alır. Kesintisiz döndürmek için:
- Panelde farklı bir adla ikinci bir anahtar oluşturun (aynı ad ve sahiple aynı anda tek etkin anahtar olabilir).
- Yeni anahtarı gizli yönetim aracınıza yazın ve hizmetlerinizi yeniden başlatarak dağıtın.
- Hiçbir hizmet eski anahtarı kullanmadığını doğrulayın (işlem geçmişi ve kendi günlükleriniz).
- Eski anahtarı iptal edin.
Sızıntı olursa
- Sızan anahtarı hemen iptal edin (etkisi bir sonraki istektedir).
- Yeni anahtarı yukarıdaki sırayla dağıtın.
- İşlem geçmişinde ve kendi günlüklerinizde, sızıntı anından sonraki beklenmeyen işlemleri (cüzdan aktarımları, eşya üretimi, webhook kaydı) arayın.
- Anahtarın erişebildiği ortamlarda webhook uç noktalarını gözden geçirin; saldırgan bir uç nokta kaydetmiş olabilir (kayıtlı sırlar bir kez gösterilir, ama yeni kayıt yeni sır üretir).
2. Oyuncu oturumu ve tarayıcı güvenliği
- Oyuncu girişinden sonra sunucu üç çerez yazar:
__Secure-crx.session(oturum),__Secure-crx.refresh(yenileme) ve__Secure-crx.csrf(CSRF). Üçü deHttpOnly,Secure,Path=/veSameSite=Laxtaşır (betikler okuyamaz); CSRF jetonunun betiğe verilen düz değeri çerezde değil, yanıt başlığı ve gövdesindedir.__Secure-öneki HTTPS gerektirir: düz HTTP’de oturum kurulamaz. - Oturum 30 gün kayar: oyuncu günde en az bir kez istek yaparsa uzar. Mutlak tavan 180 gündür: ne kadar aktif olursa olsun 180 gün sonra yeniden giriş gerekir. Açık bir realtime soketi oturumu yaşatmaz. Süreler sunucu kodunda sabittir.
- Ortam isterse tek aktif oturum kuralı açabilir (yeni giriş eskisini düşürür); bu, panelde giriş ayarlarındandır.
- Erişim JWT’si (EdDSA) oturum jetonu değildir; oyun sunucunuz onu
GET /v1/auth/jwksile çevrimdışı doğrular (sunucu doğrulaması).
CSRF
Çerez oturumunda her değiştirici (POST, PUT, PATCH, DELETE) istek iki koruma geçer:
- Origin denetimi:
Origin(ya daReferer) başlığındaki host, ortamın izinli kökenlerinden olmalıdır. Ortama bağlı bir host yalnız o ortama yönelebilir.Sec-Fetch-Site: cross-siteveSec-Fetch-Mode: navigatebirlikteyse engellenir.Originhiç göndermeyen istemciler (curl, sunucudan sunucuya) bu denetimden geçer; onları koruyan katman çift gönderimdir. - Çift gönderim (double submit):
__Secure-crx.csrfçerezi ilex-csrf-tokenbaşlığı birlikte gelmeli ve eşleşmelidir. Jeton tek kullanımlıktır ve her başarılı doğrulamada döner: yanıttaki yeniSet-Cookievex-csrf-tokendeğerini bir sonraki istek için saklayın. Yeni jeton içinGET /v1/auth/csrf.
TypeScript SDK’sı bunu otomatik yönetir; elle HTTP yazıyorsanız oturumlar ve CSRF sayfasına bakın. Hata durumu: 403 ve düz metin permission denied.
CORS ve aynı köken
Sunucu CORS başlıklarını yalnız ortam alan adı listesindeki (ve operatörün genel izinli listesindeki) https://<alan adı> origin’lerine, yalnız kamusal API’de gönderir: izinli origin aynen yansıtılır, Access-Control-Allow-Credentials: true eklenir, * kullanılmaz, panel yolu (/v1/panel/*) hiçbir koşulda CORS almaz. CORS yetki değildir: Origin, CSRF çift gönderimi ve ortam denetimleri aynen çalışır; CORS yalnız tarayıcının yanıtı okumasına izin verir. Kendi vekilinize ek CORS başlığı eklemeyin: çift başlık tarayıcıda hata verir.
Oturum çerezi SameSite=Lax olduğundan doğrudan çağrı yalnız oyun ile Open Play aynı sitede (aynı kayıtlı alan adının alt alanları) ise çalışır; farklı sitede ters vekil kullanın. Topoloji: mimari ve barındırma.
İzinli kökenler ortam alan adlarınızdır. Panelde ortam için tanımladığınız alan adı hem o ortama yönlendirmenin hem de Origin denetiminin dayanağıdır (Oyun ayarları: ortamlar). Bilinmeyen bir host’tan gelen değiştirici istek reddedilir.
3. Webhook doğrulaması
Open Play, abone olduğunuz olayları uç noktanıza imzalı bir POST ile gönderir. Her isteği doğrulamak zorundasınız; imzasız isteği işlemeyin.
| Başlık | Anlam |
|---|---|
X-Crx-Signature | t=<unix saniye>,v1=<hex> |
X-Crx-Event-Id | Olay kimliği (UUID); tekilleştirme anahtarı |
X-Crx-Event-Type | Olay türü (webhook olayları) |
İmza: HMAC-SHA256(gizli, "{t}.{ham gövde}"), küçük harfli onaltılık. Anahtar, kayıtta aldığınız whsec_… metninin kendisidir (bayt olarak). Kurallar:
- Ham gövdeyi imzalayın/doğrulayın: JSON’u çözüp yeniden yazarsanız baytlar değişir ve imza tutmaz.
- Sabit zamanlı karşılaştırma kullanın (
timingSafeEqual,hmac.compare_digest). tdeğerini kendi saatinizle karşılaştırın ve pencere dışını reddedin (5 dakika önerilir; bu alıcı politikasıdır, sunucu zorlamaz).- At-least-once: aynı olay birden çok kez gelebilir.
X-Crx-Event-Idile tekilleştirin ve işlemi tekrar-güvenli yapın. - Yalnız
2xxyanıt başarıdır. Yanıtı hızlı verin (işi kuyruğa alın); diğer durumlar yeniden denenir.
Teslimat davranışı:
| Davranış | Değer |
|---|---|
| Yeniden deneme | Üstel geri çekilme: 1 sn ile başlar, 2’nin kuvvetiyle artar, 5 dakikada tavanlanır, ±%20 sapma |
| En çok deneme | 10; sonra teslimat failed (ölü mektup kuyruğu, DLQ) olur |
| Yönlendirme (redirect) | Takip edilmez |
| Gövde sınırı | 64 KiB üstü gövde gönderilmez |
| Uç nokta adresi | Yalnız https; özel, loopback ve link-local IP adresleri ile kullanıcı bilgisi (kullanici:parola@) içeren adresler kayıtta reddedilir (SSRF koruması) |
| Gizli anahtar | Kayıt yanıtında yalnız bir kez döner; döndürme = uç noktayı silip yeniden kaydetmek |
Not
Webhook gizli anahtarını döndürmek için yerinde bir “rotate” yoktur. Kesintisiz geçiş: yeni uç noktayı (yeni gizli anahtarla) kaydedin, alıcınız ikisini birden doğrulasın, eski uç noktayı silin.
Doğrulama örneği (Node, node:crypto):
import { createHmac, timingSafeEqual } from "node:crypto";
/** İmzalı webhook isteğinin doğrulama sonucu. */
export type WebhookCheck = { ok: true; eventId: string; eventType: string } | { ok: false; reason: string };
const TOLERANCE_SECONDS = 300; // yeniden oynatma penceresi: alıcı politikasıdır, 5 dakika önerilir
/**
* Open Play webhook isteğini doğrular. `rawBody`, ayrıştırılmamış HAM bayt dizisi olmalıdır:
* JSON'u çözüp yeniden yazarsanız imza tutmaz.
* İmza: HMAC-SHA256(secret, "{t}.{ham gövde}"), başlık biçimi `t=<unix saniye>,v1=<hex>`.
*/
export function verifyWebhook(
secret: string,
rawBody: Buffer,
headers: { signature: string | undefined; eventId: string | undefined; eventType: string | undefined },
nowSeconds: number = Math.floor(Date.now() / 1000),
): WebhookCheck {
const parts = Object.fromEntries((headers.signature ?? "").split(",").map((part) => part.trim().split("=", 2) as [string, string]));
const timestamp = Number(parts["t"]);
const received = parts["v1"];
if (!Number.isInteger(timestamp) || received === undefined) return { ok: false, reason: "imza başlığı bozuk" };
if (Math.abs(nowSeconds - timestamp) > TOLERANCE_SECONDS) return { ok: false, reason: "zaman damgası pencere dışında" };
const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(received, "utf8");
if (a.length !== b.length || !timingSafeEqual(a, b)) return { ok: false, reason: "imza uyuşmuyor" };
if (headers.eventId === undefined || headers.eventType === undefined) return { ok: false, reason: "olay başlıkları eksik" };
return { ok: true, eventId: headers.eventId, eventType: headers.eventType };
}
Teslimat durumu ve DLQ’yu panelde izleyin: Webhook ve olay kuyruğu. Kayıt ve uç nokta yönetimi için webhook rehberi.
4. Sırların döndürülmesi: özet
| Sır | Nasıl döndürülür |
|---|---|
| Sunucu/istemci API anahtarı | İkinci anahtar oluştur, dağıt, eskiyi iptal et (yukarıda) |
| Webhook gizli anahtarı | Yeni uç nokta kaydet, alıcıyı ikisini kabul edecek şekilde güncelle, eskiyi sil |
| Oyuncu parolası | Oyuncu kendi parolasını değiştirir (tüm oturumlar düşer) ya da parola sıfırlama akışı (e-posta ve parola) |
| Oyuncu oturumları | Kullanıcı çıkış yapar; yönetici oyuncunun oturumlarını iptal eder (panel ya da POST /v1/auth/admin/revoke-player-sessions) |
5. Neleri günlüğe yazmayın
API anahtarı, oturum ve yenileme jetonu, CSRF jetonu, realtime bileti, webhook gizli anahtarı, OAuth state ve code değerleri, parola ve misafir gizli değeri. SDK’lar hata nesnelerinde bunları içermez; kendi günlükleme kodunuzda da isteği ham yazdırmayın.
6. Diğer notlar
- Hata ayrıntısı sızdırılmaz: sunucu
500yanıtında iç ayrıntı vermez; hesap varlığı da sızdırılmaz (yanlış parola ile bilinmeyen adres aynı yanıtı alır). - Yasaklı hesap: giriş uçlarında, sahiplik kanıtlandıktan sonra
403 account_banned; diğer uçlarda ayrım yapılmaz (hatalar). - Güvenlik şalterleri: panelde ortam başına bazı koruma kurallarını gevşetebilir ya da geri zorlayabilirsiniz; her değişiklik gerekçeyle kaydedilir (Güvenlik şalterleri).
- Eklentiler sunucuya derlenmiş güvenilir koddur; üçüncü taraf eklenti çalıştırma ve yalıtım henüz yoktur (eklentiler).
- Panel yönetici hesapları için ayrı bir kimlik hattı ve iki aşamalı doğrulama vardır; oyuncu hesaplarında iki aşamalı doğrulama yoktur.