Cloudflare'ın Container Güvenlik Açığı Bize Multi-Tenant Oyun Backend Güvenliği Hakkında Ne Öğretti
Özet olarak
Cloudflare container güvenlik açığından yola çıkarak multi-tenant oyun backend güvenliğini tespit ve düzeltme adımlarıyla öğrenin.
Container tabanlı oyun backend'iniz her maç, her lobi, her analitik toplu işlemi için yeni bir VM başlatıyor. Container kapatıldığında veriler yok olur, değil mi?
Pek sayılmaz. Eylül 2026'da Accomplish'ten güvenlik araştırmacısı Oren Yomtov, Cloudflare Containers'ta tenant'lar arası bir veri ifşası güvenlik açığı olduğunu keşfetti. Yok edilen container'lardan arta kalan disk blokları, aynı host üzerindeki alakasız workload'lar tarafından okunabiliyordu. Kurtarılan veriler arasında dizin yapıları, veritabanı sayfaları ve yapısal olarak tam SQLite veritabanları vardı.
Eğer oyun backend'lerinizi herhangi bir multi-tenant container platformunda çalıştırıyorsanız, bu güvenlik açığının kök nedenini — dm-thin alt sisteminde skip_block_zeroing adlı bir Linux depolama yapılandırması — anlamanız gerekir. Bu yazı neyin yanlış gittiğini ayrıntılarıyla anlatıyor, bugün uygulayabileceğiniz bir tespit ve düzeltme runbook'u sunuyor ve bu sınıf güvenlik açıklarının oyuncu verilerinize ulaşmasını engelleyen mimari ilkeleri ele alıyor.
Thin Provisioning Tenant'lar Arası Veri İfşasını Nasıl Oluşturur?
Cloudflare Containers, multi-tenant donanım üzerinde Firecracker micro-VM'ler içinde workload'ları çalıştırır. Her container, Linux device-mapper thin provisioning (dm-thin) tarafından desteklenen yazılabilir bir kök disk alır. Firecracker bu diski guest VM'e /dev/vdc olarak sunar.
Thin provisioning, fiziksel depolama tahsisini erteleyerek çalışır. 10 GiB'lık bir sanal disk oluşturduğunuzda neredeyse hiç fiziksel alan tüketmez. Bloklar, guest daha önce eşlenmemiş bir bölgeye yazdığında paylaşılan havuzdan talep üzerine tahsis edilir. Cloudflare'in etkilenen havuzları 64 KiB thin-block boyutu kullanıyordu.
Güvenlik açığının yaşadığı yer burası. Bir container yok edildiğinde, thin-volume eşlemeleri silinir ve fiziksel bloklar yeniden kullanım için paylaşılan havuza döner. Etkilenen havuz şu şekilde yapılandırılmıştı:
skip_block_zeroing
Bu bayrakla dm-thin, yeni tahsis edilen blokları bir sonraki tenant'ın erişimine açmadan önce sıfırlamayı atlar. 64 KiB'lık tam bir yazma, geri dönüştürülen bloğun tamamının üzerine yazar. Ancak kısmi bir yazma — diyelim ki 4 KiB — yalnızca o bölgeyi değiştirir. Geriye kalan 60 KiB, bloğun önceki sahibine ait verileri barındırabilir.
Bu teorik bir uç durum değil. Araştırmacı, dört kıtaya yayılan 24 üretim yerleşiminin 18'inde ve 22 temel düğümün 20'sinde artık bloklardan dizin yapılarını, veritabanı sayfalarını ve eksiksiz SQLite veritabanlarını kurtardı.
Kısmi Yazmalar Neden Ana Vektördür?
Yeni bir thin diskin eşlenmemiş bir bölgesini okumak sıfırlar döndürür — dm-thin, fiziksel bir blok tahsis etmeden sıfırlar sunar. Bu güvenlidir. İstismar, küçük bir yazmanın önce sıfırlamadan geri dönüştürülmüş 64 KiB'lık bir bloğun tahsisini tetiklemesi nedeniyle çalışır.
Saldırı deseni:
Workers Paid hesabında bir container oluşturun.
/dev/vdc(yazılabilir kök disk) açın.ext4 boş alanına karşılık gelen 64 KiB hizalı bölgeleri belirleyin.
Her hedef bölgeye hizalı bir 4 KiB blok yazın.
64 KiB'lık blokların tamamını geri okuyun.
Saldırganın üzerine yazmadığı yalnızca 60 KiB'ı inceleyin.
adım kritik andır. 4 KiB'lık yazma, dm-thin'in paylaşılan havuzdan fiziksel bir blok tahsis etmesine neden olur. Sıfırlama devre dışı olduğundan, kalan 60 KiB, daha önce o bloğa sahip olan kişiye ait artık verileri içerebilir.
Araştırmacının Gerçekte Doğruladıkları
Araştırmacılar, kendi test dosya sistemi bloklarını yabancı bloklardan ayırt etmek için ext4 dizin blok sağlamalarını (metadata_csum özelliği) kullandı. Altı üretim yerleşiminde:
- 5.614 test edilebilir dizin bloğu incelendi
- 0 blok araştırmacıların kendi dosya sistemine atfedildi
- 2.700 farklı yabancı dizin inode'u sağlama analiziyle tanımlandı
Kurtarılan dosya türleri çöp değildi. Oyun backend bağlamında oyuncu kayıt durumları, oturum token'ları veya envanter kayıtları içerebilecek türden, yapısal olarak anlamlı SQLite veritabanları içeriyordu.
Tespit Playbook'u: Altyapınızda Artık Veri Hasadını Bulma
Multi-tenant altyapıda container tabanlı oyun backend'leri işletiyorsanız, iki katmanlı tespit gerekir: yapılandırma denetimi ve çalışma zamanı I/O anomali analizi.
Adım 1: dm-thin Yapılandırmanızı Denetleyin
Container'larınızı çalıştıran her hostta bu betiği çalıştırın:
#!/bin/bash
set -euo pipefail
echo "=== dm-thin Pool Configuration Audit ==="
echo ""
# Find all thin pool devices on this host
for pool in $(dmsetup status --target thin-pool 2>/dev/null | awk '{print $1}'); do
echo "Pool: $pool"
TABLE=$(dmsetup table "$pool")
echo " Full table: $TABLE"
if echo "$TABLE" | grep -q "skip_block_zeroing"; then
echo " ⚠️ WARNING: skip_block_zeroing is ENABLED"
echo " Recycled blocks may retain previous tenant data."
echo " Action: Remove skip_block_zeroing from pool table."
else
echo " ✅ Block zeroing is active (default dm-thin behavior)"
fi
echo ""
done
echo "=== Thin-block size check ==="
for pool in $(dmsetup status --target thin-pool 2>/dev/null | awk '{print $1}'); do
BLOCK_SECTORS=$(dmsetup table "$pool" | grep -oP '\d+ \d+ thin-pool' | awk '{print $1}')
BLOCK_KB=$((BLOCK_SECTORS * 512 / 1024))
echo " Pool $pool: block size = ${BLOCK_KB} KiB"
if [ "$BLOCK_KB" -le 64 ]; then
echo " ⚠️ Small block size amplifies residual data exposure."
echo " Larger block sizes reduce the ratio of leftover bytes per partial write."
fi
done
Havuz yapılandırmanızda herhangi bir yerde skip_block_zeroing görünüyorsa, Cloudflare'in yamaladığı güvenlik açığı sınıfının aynısına sahipsiniz. Hemen düzeltin — planlanmış bakım penceresini beklemeyin.
Adım 2: Karakteristik I/O İmzasını İzleyin
İstismar, tespit edilebilir bir imza üretir: orantısız şekilde büyük okumaların izlediği küçük yazmalar. 4 KiB'lık bir yazma 64 KiB'lık bir blok tahsis eder; sonraki bir okuma 64 KiB'nin tamamını kurtarır. Okuma hacminin yazma hacmini >10x aştığı container telemetrisi şüphelidir.
#!/usr/bin/env python3
"""
Detect anomalous write-then-read patterns characteristic of
cross-tenant residual data harvesting in thin-provisioned containers.
Usage: python detect_residual_harvesting.py io_events.jsonl
Input: JSONL file with one event per line:
{"container_id": "abc123", "operation": "write", "size_bytes": 4096}
"""
import json
import sys
from collections import defaultdict
ANOMALY_THRESHOLD = 10.0 # read_bytes : write_bytes ratio
def analyze_io_events(events_file):
with open(events_file) as f:
events = [json.loads(line) for line in f]
containers = defaultdict(
lambda: {"writes": 0, "reads": 0, "write_bytes": 0, "read_bytes": 0}
)
for event in events:
cid = event.get("container_id", "unknown")
op = event.get("operation")
size = event.get("size_bytes", 0)
if op == "write":
containers[cid]["writes"] += 1
containers[cid]["write_bytes"] += size
elif op == "read":
containers[cid]["reads"] += 1
containers[cid]["read_bytes"] += size
print("=" * 60)
print("CONTAINERS WITH ANOMALOUS READ/WRITE RATIOS")
print("=" * 60)
flagged = 0
for cid, stats in sorted(containers.items()):
if stats["write_bytes"] == 0:
continue
ratio = stats["read_bytes"] / stats["write_bytes"]
if ratio > ANOMALY_THRESHOLD:
flagged += 1
print(f"\n🚩 Container: {cid}")
print(f" Writes: {stats['writes']:,} ops | "
f"{stats['write_bytes']:,} bytes")
print(f" Reads: {stats['reads']:,} ops | "
f"{stats['read_bytes']:,} bytes")
print(f" Ratio: {ratio:.1f}x — investigate")
if flagged == 0:
print("\n✅ No anomalous containers detected.")
print(f"\nScanned {len(containers)} containers. Flagged: {flagged}")
return flagged
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python detect_residual_harvesting.py <io_events.jsonl>")
sys.exit(1)
sys.exit(1 if analyze_io_events(sys.argv[1]) > 0 else 0)
Cloudflare olayında, araştırmacıların proof-of-concept'i tam olarak bu imzayı üretti. Cloudflare'in güvenlik ekibi, PoC'den tespit imzaları oluşturdu, bunları geçmiş disk I/O telemetrisine uyguladı ve eşleşen tek etkinliğin araştırmacılardan ve Cloudflare'in kendi mühendislerinden, yetkili doğrulama sırasında geldiğini doğruladı. Üçüncü taraf istismarı bulunamadı.
Ders şu: container I/O telemetrisi topluyorsanız, geriye dönük denetim yapabilirsiniz. Toplamıyorsanız, kör uçuyorsunuz.
Düzeltme Runbook'u
Denetiminiz skip_block_zeroing veya benzeri bir maruziyet ortaya çıkarırsa, Cloudflare'in uyguladığı düzeltme sırası aşağıda — ve sıralama önemlidir.
Adım 1: Tüm Havuzlarda Blok Sıfırlamayı Yeniden Etkinleştirin
skip_block_zeroing öğesini dm-thin havuz yapılandırmanızdan kaldırın. Bu, thin-pool cihazında çalışma zamanı değişikliğidir, ancak yalnızca yeni tahsisleri korur. Halihazırda çalışan container'lara eşlenmiş bloklar ve önbelleğe alınmış image snapshot'ları savunmasız kalır.
# This is a simplified representation — actual dm-thin
# pool recreation commands vary by your orchestration layer.
# The core principle: restart the pool WITHOUT skip_block_zeroing.
dmsetup reload <pool-name> --table "0 <size> thin-pool <meta_dev> <data_dev> <data_block_size> 0"
dmsetup suspend <pool-name>
dmsetup resume <pool-name>
Cloudflare bu düzeltmeyi ilk rapordan yaklaşık 6 saat içinde birleştirdi. Araştırmacılar, bu değişiklikten sonra PoC'nin çalışmayı durdurduğunu bağımsız olarak doğruladı.
Adım 2: Çalışan Tüm Container Disklerini Emekli Edin
Etkin thin cihazlara halihazırda eşlenmiş bloklar, (sıfırlanmamış) içeriklerini korur. Artık verileri ortadan kaldırmanın tek yolu, her container diskini yok edip yeniden oluşturmaktır.
Cloudflare, yoğun olmayan saatlerde host'ları boşalttı ve her hosttaki VM'leri yeniden başlattı. Bu, kademeli bir operasyondur — bir oyun backend'i için bunu en düşük trafik pencerenizde planlayın. Host başına kısa bir kesinti bekleyin. Matchmaking katmanınız sorunsuz devri destekliyorsa, boşaltılan hostlardaki oyuncular bağlantıları kesilmeden taşınır.
Adım 3: Önbelleğe Alınmış Image Snapshot'larını Temizleyin
Bu adımı atlamak kolaydır. Container orkestrasyon katmanları, hızlı container başlangıcı için OCI image katmanlarını genellikle dm-thin snapshot'ları olarak önbelleğe alır. Bu önbelleğe alınmış snapshot'lar düzeltmeden önce oluşturuldu ve kullanılmayan bölgelerde (ext4 boş alanı dahil) artık veriler içerebilir. Önbelleğe alınmış bir katmanı devralan yeni bir container, yeni bir blok tahsis etmeden /dev/vdc üzerinden artık baytları okuyabilir.
Cloudflare, etkilenen filodaki tüm ön-mitigasyon önbelleğe alınmış snapshot'ları kaldırdı ve temizliği ilk rapordan 15 gün sonra tamamladı. Önbelleğe alınan katmanlar, sıfırlanmış tahsisler kullanılarak yeniden oluşturuldu.
Yeniden oluşturma sırası önemlidir. Sıfırlamayı etkinleştirmeden önce önbellekleri temizlerseniz, sıfırlanmamış blokları doğrudan yeni snapshot'lara geri itmiş olursunuz.
Adım 4: Düzeltmenin Geçerli Olduğunu Doğrulayın
Düzeltmeden sonra, tespit betiğini en az 48 saat boyunca yeni I/O telemetrisine karşı çalıştırın. Yazma/okuma oranlarını yama öncesi taban çizginizle karşılaştırın. Karakteristik yüksek oran deseni tamamen kaybolmalıdır.
Ayrıca yeniden başlatma sonrası her hosttaki dm-thin tablosunu doğrulamalısınız:
# Confirm no host still has skip_block_zeroing
dmsetup table 2>/dev/null | grep -q "skip_block_zeroing" && \
echo "CRITICAL: Host $(hostname) still has skip_block_zeroing" || \
echo "OK: $(hostname) — block zeroing active"
Bu Sınıf Güvenlik Açığını Önlemek için Mimari İlkeler
Cloudflare olayı, daha geniş bir multi-tenant depolama izolasyonu hatası sınıfının yalnızca bir örneğidir. Oyun backend'inizi koruyan ilkeler şunlardır — ister kendi container'larınızı yönetin ister bir platforma güvenin.
İlke 1: Multi-Tenant Havuzlarda Blok Sıfırlamayı Asla Devre Dışı Bırakmayın
skip_block_zeroing bayrağı performans için vardır — her tahsisatta 64 KiB blokları sıfırlamak I/O yükü ekler. Yalnızca sizin kodunuzun çalıştığı single-tenant donanımda bu ödünleşim kabul edilebilir olabilir. Container'ların tenant'lar arasında geri dönüştürüldüğü paylaşılan altyapıda bu bir güvenlik kusurudur.
Kural: Blokları birden fazla tenant kimliğine tahsis eden her dm-thin havuzunda blok sıfırlama etkin olmalıdır. Bunu infrastructure-as-code katmanında zorunlu kılın ki hiçbir host onsuz sağlanamasın.
İlke 2: Container Disklerini Geçici ve Tehlikeli Olarak Ele Alın
Oyun backend'iniz bir container'ın diskini başlangıçta temiz varsaymamalıdır. Blok sıfırlama etkin olsa bile, önbellek katmanları, snapshot devralma ve çekirdek hatalarıyla ilgili uç durumlar vardır.
Container entrypoint'inizi, önyüklemede yazılabilir birimi biçimlendirecek veya silecek şekilde yazın:
#!/bin/bash
# Ensure a clean writable disk on container start
if [ -b /dev/vdc ]; then
# Overwrite with zeros (slow but thorough)
# For a game backend, the writable disk is typically small (1-4 GiB)
dd if=/dev/zero of=/dev/vdc bs=1M count=4096 status=progress
# Create a fresh filesystem
mkfs.ext4 -F /dev/vdc
mount /dev/vdc /workspace
fi
Bu, container başlangıcına birkaç saniye ekler. 5–45 dakika süren bir oyun maçı için bu kabul edilebilir. Saniyenin altında soğuk başlangıç gereksinimleri için daha hızlı yaklaşımı kullanın: blkdiscard (TRIM'i tetikler ve depolama arka ucuna bağlı olarak blokları sıfırlayabilir) ile mkfs.ext4 -F kombinasyonu.
İlke 3: I/O Oranlarını Güvenlik Sinyali Olarak İzleyin
Çoğu container izleme sistemi CPU, bellek ve ağa odaklanır. Güvenlik telemetrinize disk I/O'yu ekleyin. Okunan baytların yazılan baytlara oranı, artık veri hasadı saldırıları için güçlü bir sinyaldir — meşru oyun workload'ları dengeli desenlerde yazar ve okur. 4 KiB parçalar yazan ve ardından bölge başına 60+ KiB geri okuyan bir container anormaldir.
Backend'iniz zaten container yaşam döngüsü olaylarını günlüyorsa, olası bir olayın patlama yarıçapını daraltmak için I/O anomali lerini tenant kimliği ve oluşturma zaman damgalarıyla ilişkilendirebilirsiniz. Bu telemetri altyapısını kendileri yönetmek istemeyen oyun geliştiricileri için horizOn gibi platformlar bu yetenekleri kutudan çıkar çıkmaz sunar — yani tesisat kurmak yerine oyun mantığına daha fazla zaman harcarsınız.
İlke 4: Oyuncu Verileri için Derinlemesine Savunma Uygulayın
Container diskiniz sızdırsa bile, üzerindeki veriler şifrelenmişse veya anahtar olmadan anlamsızsa hasar sınırlıdır. Oyuncu kayıt durumlarını, oturum token'larını ve envanter verilerini şifreli olarak saklayın. Şifreleme anahtarı, container diskinde değil, bir secrets manager'da bulunmalıdır.
Bu, Star Citizen veri ihlali ve oyun backend'lerini ihlallere dayanıklı şekilde nasıl mimarilendireceğiniz analizimizde ele aldığımız ilkeyle aynıdır — derinlemesine savunma, herhangi bir katmanın eninde sonunda başarısız olacağını varsaymak demektir.
En İyi Uygulamalar Kontrol Listesi
Dağıtımdan önce her dm-thin havuz yapılandırmasını denetleyin. Container orkestrasyon hattınıza,
skip_block_zeroingmevcutsa başarısız olan bir ön uçuş kontrolü ekleyin. Bu, tüm bir güvenlik açığı sınıfını önleyen beş satırlık bir CI kontrolüdür.Container disk I/O telemetrisini tenant ilişkilendirmesiyle toplayın. Gözlemlemediğiniz istismarı tespit edemezsiniz. Her container için yazma/okuma hacimlerini, tenant ID ve host ID ile ilişkilendirerek günlüğe kaydedin. Geriye dönük inceleme için en az 30 gün saklayın.
Önyüklemede container yazılabilir birimlerini sıfırlayın veya silin. Yalnızca depolama katmanına güvenmeyin. Başlangıçta taze bir
mkfs.ext4, 2–5 GiB yazma yükü ekler, ancak hiçbir artık verinin container geri dönüşümünden kurtulamayacağını garanti eder.Güvenlik yamaları sırasında container'ları boşaltın ve geri dönüştürün, yalnızca sonrasında değil. Blok sıfırlamayı etkinleştirmek yalnızca yeni tahsisleri korur. Çalışan container'lardaki mevcut eşlemeler ve önbelleğe alınmış image snapshot'ları açıkça temizlenmelidir. Düzeltmeyi her zaman filo genelinde bir geri dönüşümle takip edin.
Hassas oyuncu verilerini harici anahtar yönetimiyle şifreleyin. Artık bir blok sızarsa, anahtarı olmayan şifreli metin işe yaramaz. horizOn'un hesaba bağlı Cloud Save'i aracılığıyla yönetilen oyuncu kayıt durumları, revizyon bilincine sahip yazmalar ve istemci tarafı çakışma çözümü kullanır — ancak container'ları kendi kendinize barındırıyorsanız, temel disk izolasyonu hâlâ sizin sorumluluğunuzdadır.
Zaman Çizelgesi: Cloudflare Nasıl Yanıt Verdi?
Referans olarak, iyi kaynaklara sahip bir ekibin kritik bir altyapı güvenlik açığında ne kadar hızlı hareket edebileceğini görün:
| Zaman (UTC) | Eylem |
|---|---|
| 4 Eylül 15:26 | Araştırmacı HackerOne üzerinden raporlar |
| 4 Eylül 18:45 | Güvenlik olayı açıldı, üretim kurulumu doğrulandı |
| 4 Eylül 21:27 | Yeniden kullanım testiyle çalışma zamanı düzeltmesi birleştirildi |
| 4 Eylül 23:15 | Kademeli dağıtım başlıyor |
| 7 Eylül 06:13 | Kademeli dağıtım tamamlandı, temizlik başlıyor |
| 14 Eylül 10:50 | Araştırmacı PoC'nin artık çalışmadığını doğruluyor |
| 19 Eylül 15:03 | Ön-mitigasyon önbelleğe alınmış tüm snapshot'lar temizlendi |
Bu, raporlamadan doğrulanmış kök nedene kadar 3 saatten az ve birleştirilmiş düzeltmeye kadar 8 saatten az sürdü. Filo genelindeki temizlik 15 gün sürdü — küresel bir altyapıda kademeli operasyonlar için normal.
Hız önemlidir. Güvenlik açığı raporu ile düzeltme arasındaki her saat, istismarın mümkün olduğu bir saattir. Kendi container altyapınızı işletiyorsanız, runbook'u ihtiyacınız olmadan önce oluşturun.
Alt Satır
Cloudflare container güvenlik açığı, kriptografik anlamda bir zero-day değildi. Multi-tenant workload'lar için uygunsuz olan, güvenlik yerine performansı seçen bilinen bir depolama yapılandırma ödünleşimiydi. Düzeltme, tek bir yapılandırma değişikliği ve filo genelinde geri dönüşümden ibaretti.
Oyun backend'lerinizi paylaşılan container altyapısında çalıştırıyorsanız, dm-thin havuzlarınızı bugün denetleyin. Tespit betiğini I/O telemetrinize karşı çalıştırın. Container disk izolasyonu, filo geri dönüşümü ve I/O telemetri hatlarını kendiniz yönetmek istemiyorsanız, horizOn kimlik doğrulama, kilitlenme raporları, cloud save, lider tabloları ve oyununuzun ihtiyaç duyduğu diğer backend hizmetlerini üstlenir — böylece gece 3'te üretimde depolama katmanı güvenliğini hata ayıklamak yerine oyununuzu piyasaya sürmeye odaklanabilirsiniz.
Kaynak: Cloudflare, Containers'ta tenant'lar arası veri ifşası güvenlik açığını nasıl ele aldı