Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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ürNerede tutulurNe 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ülebilirHassas 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. Authorization baş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 expiresAt ayarlanabilir; süresi dolan anahtar reddedilir.

Yaşam döngüsü

Anahtarlar panelde Oyun ayarları: API anahtarları ekranında yönetilir (API anahtarları):

EylemEtki
OluşturYeni anahtar; gizli değer bir kez gösterilir
Devre dışı bırak / etkinleştirGeç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
SilYumuş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:

  1. Panelde farklı bir adla ikinci bir anahtar oluşturun (aynı ad ve sahiple aynı anda tek etkin anahtar olabilir).
  2. Yeni anahtarı gizli yönetim aracınıza yazın ve hizmetlerinizi yeniden başlatarak dağıtın.
  3. Hiçbir hizmet eski anahtarı kullanmadığını doğrulayın (işlem geçmişi ve kendi günlükleriniz).
  4. Eski anahtarı iptal edin.

Sızıntı olursa

  1. Sızan anahtarı hemen iptal edin (etkisi bir sonraki istektedir).
  2. Yeni anahtarı yukarıdaki sırayla dağıtın.
  3. İş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.
  4. 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). Üçü de HttpOnly, Secure, Path=/ ve SameSite=Lax taşı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/jwks ile çevrimdışı doğrular (sunucu doğrulaması).

CSRF

Çerez oturumunda her değiştirici (POST, PUT, PATCH, DELETE) istek iki koruma geçer:

  1. Origin denetimi: Origin (ya da Referer) 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-site ve Sec-Fetch-Mode: navigate birlikteyse engellenir. Origin hiç göndermeyen istemciler (curl, sunucudan sunucuya) bu denetimden geçer; onları koruyan katman çift gönderimdir.
  2. Çift gönderim (double submit): __Secure-crx.csrf çerezi ile x-csrf-token başlığı birlikte gelmeli ve eşleşmelidir. Jeton tek kullanımlıktır ve her başarılı doğrulamada döner: yanıttaki yeni Set-Cookie ve x-csrf-token değerini bir sonraki istek için saklayın. Yeni jeton için GET /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ıkAnlam
X-Crx-Signaturet=<unix saniye>,v1=<hex>
X-Crx-Event-IdOlay kimliği (UUID); tekilleştirme anahtarı
X-Crx-Event-TypeOlay 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:

  1. Ham gövdeyi imzalayın/doğrulayın: JSON’u çözüp yeniden yazarsanız baytlar değişir ve imza tutmaz.
  2. Sabit zamanlı karşılaştırma kullanın (timingSafeEqual, hmac.compare_digest).
  3. t değerini kendi saatinizle karşılaştırın ve pencere dışını reddedin (5 dakika önerilir; bu alıcı politikasıdır, sunucu zorlamaz).
  4. At-least-once: aynı olay birden çok kez gelebilir. X-Crx-Event-Id ile tekilleştirin ve işlemi tekrar-güvenli yapın.
  5. Yalnız 2xx yanı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 deneme10; 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 adresiYalnız https; özel, loopback ve link-local IP adresleri ile kullanıcı bilgisi (kullanici:parola@) içeren adresler kayıtta reddedilir (SSRF koruması)
Gizli anahtarKayı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ırNası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 500 yanı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.