Ne oldu
SafeState'in araştırmacı analizlerini derleyen yazısına göre saldırı kabaca şöyle ilerledi:
- Polymarket'in sitesi, DebugBear'ın CDN'inden bir gerçek kullanıcı izleme (RUM) script'i yüklüyordu. Bu olağan bir kurulum. Binlerce site onlarca tedarikçiyle aynısını yapıyor.
- Saldırgan bu dosyayı kötü amaçlı JavaScript ile değiştirdi. Tarayıcı dosyayı her seferinde doğrudan tedarikçiden taze olarak çektiği için her ziyaretçi yeni sürümü anında aldı.
- Script, aktif bir Polymarket oturumu olup olmadığını kontrol etti, tarayıcı depolamasından hesap verilerini, cüzdan bilgilerini ve işlem API kimlik bilgilerini okudu, ardından Polymarket'in kendi imzalama akışını kullanarak pozisyonları taşıdı ya da varlıkları saldırganın cüzdanına sattı.
Üzerinde durmaya değer ayrıntı şu: araştırmacılar donanım cüzdanlarının ve güçlü parolaların işe yaramadığını belirtti, çünkü script'in hiçbir zaman bir özel key'e ihtiyacı olmadı. Kullanıcının kendi tarayıcı oturumunun içinden, giriş yapmış kullanıcı gibi davrandı.
Aynı rapora göre Polymarket'in çözümü, tedarikçi script'ini kaldıran bir production commit'iydi. İşlemler aynı öğleden sonra yeniden başladı. SafeState ayrıca bunun, platformu bu yıl vuran ilk tedarikçi script'i olayı olmadığını da not ediyor.
Bu, uygulaman için neden önemli
cdn.some-vendor.com adresini gösteren bir script etiketi, kendi kodunla aynı yetkiyle çalışır. DOM'u okuyabilir, localStorage ve sessionStorage'ı okuyabilir, kullanıcının kimlik bilgileriyle API'ni çağırabilir ve sayfada gösterilenleri değiştirebilir. "Senin kodun" ile "lansman haftasında yapıştırdığın analitik snippet'i" arasında hiçbir sandbox yok.
Bu durum hızlı yayınlanan ve yapay zekâyla geliştirilen uygulamalar için özellikle önemli, çünkü:
- Script'ler birikir. Her "analitik ekle", "sohbet balonu ekle", "hata takibi ekle" prompt'u genellikle bir etiket daha ekler. Sonradan pek az kişi bunları kaldırır.
- Token'lar tarayıcı depolamasında durur. Birçok başlangıç şablonu oturum ya da API token'ını localStorage'da tutar; sayfadaki herhangi bir script onu okuyabilir.
- Genellikle bir Content-Security-Policy yoktur. O olmadan, enjekte edilen ya da değiştirilen herhangi bir script her yerden daha fazla kod yükleyebilir ve verileri her yere gönderebilir.
Tedarikçilerinin build pipeline'larını denetleyemezsin. Ama kötü bir script'in verebileceği zararı sınırlayabilirsin.
Uygulamanı nasıl kontrol edersin
1. Sayfalarının yüklediği her script'i listele. Uygulamanı aç, ardından DevTools'ta Network sekmesine geç, JS'e göre filtrele ve sayfayı yenile. Ya da her önemli sayfada konsolda şunu çalıştır:
[...document.scripts].map(s => s.src || '(inline)').filter(Boolean)Sana ait olmayan her alan adını not et. Bunu yalnızca pazarlama sitesinde değil, giriş yapılmış sayfalarda da yap. Etiket yöneticileri, HTML kaynağında görünmeyen ek script'ler yükleyebilir.
2. Bu etiketlerden hangilerinin integrity özniteliği olduğunu kontrol et. Konsolda:
[...document.querySelectorAll('script[src]')]
.filter(s => !s.src.startsWith(location.origin))
.map(s => ({ src: s.src, sri: !!s.integrity }))3. Yanıt başlıklarına bak. curl -sI https://yourapp.com komutunu çalıştır ve Content-Security-Policy'yi ara. Yoksa ya da nonce olmadan script-src * veya 'unsafe-inline' içeriyorsa, pek bir şeyi sınırlamıyor demektir.
4. Oturumunun nerede durduğunu kontrol et. DevTools'ta Application altında Local Storage ve Session Storage'a bak. Orada bir access token, refresh token ya da API key görüyorsan sayfadaki herhangi bir script onu okuyabilir; Polymarket'teki kötü amaçlı yük de tam olarak bunu yaptı.
Nasıl düzeltirsin
İhtiyacın olmayanı kaldır. En yüksek getirili adım bu. SafeState'in site işletmecilerine önerileri, üçüncü taraf script'lerin envanterini tutmak ve hesap, ödeme ve işlem sayfalarından gereksiz etiketleri temizlemekle başlıyor. Giriş, faturalama ve ayarlar sayfaların nadiren bir ısı haritası aracına ihtiyaç duyar.
Tuttuklarını Subresource Integrity (SRI) ile sabitle ve doğrula. Tedarikçi sürümlü dosyalar sunuyorsa belirli bir sürüme referans ver ve bir hash ekle. Dosya değişirse tarayıcı onu çalıştırmayı reddeder:
<script
src="https://cdn.example-vendor.com/sdk/4.2.1/sdk.min.js"
integrity="sha384-REPLACE_WITH_REAL_HASH"
crossorigin="anonymous"></script>Hash'i, incelediğin dosyanın tam kendisinden kendin üret:
curl -s https://cdn.example-vendor.com/sdk/4.2.1/sdk.min.js \
| openssl dgst -sha384 -binary | openssl base64 -AÖdünleşimin farkında ol: SRI, tedarikçilerin sürümsüz bir URL'de yerinde güncellediği script'leri bozar ve bu, izleme ve analitik snippet'lerinde yaygındır. Bunlar için ya bilinçli olarak güncellediğin sabitlenmiş bir kopyayı kendin barındır ya da tedarikçinin canlı dosyasına güvendiğini kabul et ve onu hassas sayfalardan uzak tut.
Bir Content-Security-Policy ekle. CSP, tarayıcıya hangi kaynakların script sunabileceğini ve sayfanın hangi kaynaklara veri gönderebileceğini söyler. Bir Next.js ya da Express uygulaması için başlangıç noktası şöyle olabilir:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example-vendor.com;
connect-src 'self' https://api.yourapp.com;
frame-ancestors 'none';
base-uri 'self';
object-src 'none'CSP'nin burada sana ne kazandırdığı konusunda gerçekçi ol: tedarikçinin kendi alan adına izin verilmişse CSP, oradan sunulan kötü amaçlı bir dosyayı durdurmaz. CSP'nin yaptığı, o script'in yeni alan adlarından kod çekmesini ya da çalınan verileri connect-src'de olmayan rastgele uç noktalara göndermesini engellemektir. Neyin bozulduğunu görebilmek için önce Content-Security-Policy-Report-Only ile devreye al. Content-Security-Policy ekleme rehberimiz bunu adım adım anlatıyor.
Oturum token'larını JavaScript'in erişiminden çıkar. Oturum token'larını HttpOnly, Secure ve SameSite olarak işaretlenmiş cookie'lere taşı; böylece sayfa script'leri onları okuyamaz. Bu, sayfa açıkken kötü amaçlı bir script'in kullanıcı adına istek yapmasını engellemez, ama token'ların çalınıp başka yerde yeniden kullanılmasını engeller. Bkz. Secure, HttpOnly ve SameSite cookie bayrakları.
Kendi frontend key'lerini temiz tut. Bir script bundle'ını okuyabiliyorsa, içine koyduğun her secret'ı da okuyabilir ve botlar zaten sızan key'lerin ve .env dosyalarının peşinde. İstemci tarafı JavaScript'te açığa çıkan API key'leri rehberimize göz at, production'da source map yayınlamadığından emin ol; daha önce bir korku yaşadıysan, uygulamanın secret'larını kesinti olmadan döndürme yazımız temizliği anlatıyor.
Özet kontrol listesi
DevTools'u kapatmadan önce şunları yaptığından emin ol:
- Herkese açık ve giriş yapılmış sayfalarındaki her üçüncü taraf script'i listele.
- Artık kullanmadığın script'leri, özellikle giriş, faturalama ve hesap sayfalarından kaldır.
- Tedarikçilerin desteklediği yerlerde sürümleri sabitle ve integrity hash'leri ekle.
- Sabitlenemeyen script'leri kendin barındır ya da hassas sayfalardan uzak tut.
- Sıkı script-src ve connect-src içeren bir Content-Security-Policy yayınla.
- Oturum token'larını localStorage'da değil, HttpOnly cookie'lerde tut.
- Biri (ya da bir yapay zekâ ajanı) yeni bir etiket eklediğinde bu denetimi yeniden yap.
Nerede durduğunu öğren
Hızlı bir başlangıç noktası istiyorsan AppSafe, bir Content-Security-Policy ayarlanıp ayarlanmadığı dahil güvenlik başlıklarını kontrol eder; doğrulanmış tam taraması ise Subresource Integrity ve güvenlik açığı bulunan JavaScript kütüphaneleri gibi frontend sorunlarına bakar. Bir raporun nasıl göründüğünü örnek raporda görebilir ya da bunu yapay zekâyla geliştirilen uygulamalar için lansman öncesi kontrol listemizle birlikte kullanabilirsin.
Polymarket, işlemleri durdurduktan sonra yaklaşık bir saat içinde nedeni buldu. Birçok küçük ekibin değişen bir script'i fark etmesi çok daha uzun sürer. Bugün uygulaman için ücretsiz bir AppSafe taraması çalıştır ve nerede durduğunu öğren.
