- Açık kaynak paketi @ctrl/tinycolor'ı içeren çok sayıda npm paketi kötü amaçlı sürümlerle enfekte oldu; bunun nedeni, ortak kullanılan bir depodaki GitHub Actions workflow'u üzerinden npm token'ının çalınmasıydı
- Saldırgan, geniş yetkilere sahip npm token'ını kullanarak yaklaşık 20 pakete kötü amaçlı kod dağıttı; bunlar arasında @ctrl/tinycolor haftalık 2 milyon indirmeye ulaştığı için etkisi büyüktü
- Enfekte sürümler
postinstallaşamasında kötü amaçlı payload çalıştırdı; GitHub ve npm güvenlik ekipleri hızla müdahale ederek kaldırma ve temizlik işlemlerini gerçekleştirdi - Yazar, tekrarını önlemek için Trusted Publishing (OIDC) geçişi, token yetkilerinin en aza indirilmesi, 2FA zorunluluğu ve pnpm özelliklerinden yararlanılması gibi güçlendirilmiş bir güvenlik planı hazırladı
- Bu olay, yazılım tedarik zinciri güvenliğinin kırılganlığını gösteriyor ve npm ekosistemi genelinde güvenlik özelliklerinin iyileştirilmesiyle güvenlik pratiklerinin değişmesi gerektiğini ortaya koyuyor
TL;DR
- Kötü amaçlı bir GitHub Actions workflow'u paylaşılan bir depoya push edilerek npm token'ı çalındı
- Bu token ile saldırgan 20 paketin kötü amaçlı sürümlerini yayımladı; bunlar arasında @ctrl/tinycolor yüksek indirme sayısı nedeniyle daha büyük etki yarattı
- Kişisel hesaplar veya repolar doğrudan ele geçirilmedi; ayrıca phishing ya da yerel kötü amaçlı yazılım kurulumu da olmadı
- GitHub/npm güvenlik ekiplerinin hızlı müdahalesiyle kötü amaçlı sürümler kaldırıldı, ardından cache'i temizlemek için temiz sürümler yeniden yayımlandı
Olay nasıl fark edildi? (How I Found Out)
- 15 Eylül öğleden sonra topluluk üyesi Wes Todd, Bluesky DM üzerinden sorunu bildirdi
- GitHub/npm güvenlik ekipleri etkilenen paketlerin listesini çoktan toparlamış ve kaldırma sürecini başlatmıştı
- İlk ipucu olarak paylaşılan kötü amaçlı branch adı 'Shai-Hulud'du; bu ad Dune evrenindeki kumsolucanından geliyor
Gerçekte ne oldu? (What Actually Happened)
- Uzun zaman önce birlikte çalışılan angulartics2 deposunda, hâlâ admin yetkisine sahip bir işbirlikçi bulunuyordu
- Bu depoda saklanan npm token'ı, kötü amaçlı bir GitHub Actions workflow'u tarafından çalındı
- Saldırgan bu token ile @ctrl/tinycolor dahil yaklaşık 20 paketi yayımladı
- GitHub/npm güvenlik ekipleri kötü amaçlı sürümleri hızla kaldırdı ve yazar güvenilir yeni sürümleri tekrar yayımladı
Etki (Impact)
- Kötü amaçlı sürümler kurulduğunda postinstall script'i çalışarak güvenlik tehdidi oluşturdu
- Etkilenen kullanıcıların StepSecurity'nin anlık müdahale rehberine başvurması öneriliyor
Yayınlama ortamı ve müdahale planı (Publishing Setup & Interim Plan)
- Daha önce otomatik yayınlama için semantic-release + GitHub Actions kombinasyonu kullanılıyordu
- npm'nin provenance özelliği kullanılmış olsa da, geçerli bir token'a sahip saldırganı engelleyemedi
- İleride statik token'ları kaldırmak için Trusted Publishing (OIDC) kullanılacak
- Şu anda tüm token'lar iptal edildi; ayrıca 2FA zorunlu hale getirildi, yalnızca granular yetkili token'lara izin veriliyor ve pnpm'in minimumReleaseAge özelliği gibi ek güvenlik önlemleri değerlendiriliyor
İdeal iyileştirmeler (Publishing Wishlist)
- npm hesap düzeyinde OIDC tabanlı Trusted Publishing'i zorlama seçeneği sunulmalı
- provenance eksik olduğunda yayını engelleme özelliği ile semantic-release ve OIDC'nin tam entegrasyonu sağlanmalı
- GitHub arayüzünde 2FA tabanlı manuel onaylı yayınlama özelliği sunulması isteniyor
- Pro abonelik olmadan da GitHub Environments düzeyindeki koruma özellikleri kullanılabilmeli
- npm paket sayfalarında postinstall script'i bulunup bulunmadığı gösterilmeli ve silinen sürümlerin neden kaldırıldığı açıklanmalı
1 yorum
Hacker News görüşleri
Söz konusu depoda hâlâ GitHub Actions secret’ları, yani geniş kapsamlı yayınlama yetkisine sahip npm token’ları duruyordu
Trusted Publishing’in avantajlarından biri, artık uzun süre geçerli yayınlama token’ları kullanmak zorunda olmamanızdır
Artık yalnızca CI VM üzerinde kısa süreli oluşturulan token’lar kullanılıyor ve bu token’lar sadece 15 dakika geçerli
Bu yaklaşım zaten PyPI, npm, Cargo, Homebrew gibi birçok ekosistemde uygulanıyor
Yayın süreci aslında biraz daha kolaylaştığı için herkesin denemesini tavsiye ederim
Belgeler hâlâ belirsiz geliyorsa her zaman yardım isteyebilirsiniz
Ekosistem yöneticileri de bu özelliğin yaygınlaşmasını açıkça istiyor
Trusted Publishing resmi belgeleri
npm’de artık Trusted Publishing kullanılabildiğini bunu vesileyle ilk kez öğrenmiş oldum
İlgili duyuru
Bu hafta sonu hemen kurmayı planlıyorum
Artık bunun gibi özellikleri kullanan projeleri depoda gösteren bir işaret olsa iyi olurdu
Böylece bunu kullanmayan bağımlılık paketlerini kolayca engelleyebilirdik
Otomatik dağıtım sürecine MFA’nın (çok faktörlü kimlik doğrulama) dahil edilmesi konusu yeterince dikkat çekmiyor gibi geliyor
CI workflow ile yayın yapıp MFA istemiyle yayını onaylamak sorun değil, ama en son baktığımda bunun için kod sağlamak amacıyla bir HTTPS tüneli açmak gerekiyordu ve bu da işleri karmaşıklaştırıyordu
npm ya da GitHub’ın CI sırasında MFA kodunu kolayca sağlama ve onaylama yolunu doğrudan sunmasını isterdim
Paket yayınlamanın 2 aşaması var: paketi npmjs’e yükleme aşaması ve onu gerçekten kullanıcılara açma aşaması
Şu anda bu ikisi tek bir işlem içinde birleşmiş durumda
Bence bunları ayırmak daha doğru olur; CI sistemi otomatik olarak sadece build alıp yükleme yapsın
Yüklenen paketin gerçekten dağıtıma çıkması içinse bir insanın npmjs sitesine doğrudan giriş yapıp elle publish etmesi ve MFA’dan geçmesi gerekir
Aslında paket publish etmenin kendisi gereksiz bir kavram olabilir mi diye düşünüyorum
Eğer VCS “gerçek kaynak” ise, ayrı bir yayınlama süreci olmadan doğrudan kullanılabilmeli
Go dili gerçekten bunu yapıyor
Paketleri doğrudan URL tabanlı import ediyor ve sürümlemeyi tag’lerle yönetiyor
Böylece yalnızca VCS’ye güvenmek yeterli oluyor ve ek saldırı yüzeyi azalıyor
Ayrı arşiv dosyalarını diff etmek yerine sadece commit düzeyinde inceleme yapmak yeterli
Sorun, depo taşındığında import yolunun değişmesi; ama bu da bir bakıma avantaj sayılabilir
Bunun dışında ayrı bir yayınlama aşamasının ne fayda sağladığını pek bilmiyorum
Sanki eski FTP ile
tararşivi yüklenen dönemlerden kalma bir kalıntı gibiDaha önce angulartics2 adlı paylaşımlı bir depoda çalışmıştım
Orada hâlâ geniş yayınlama yetkisine sahip npm token’ı içeren GitHub Actions secret’ları vardı
İş birliği yaptığımız kişilerden birinin birden çok projede yetkisi vardı ve birden fazla paketin aynı anda etkilenmesinin nedeni de muhtemelen buydu
Shai-Hulud adlı yeni bir branch, kötü amaçlı github action workflow ile birlikte force push edildi
Yönetici yetkisine sahip bir contributor olduğu için inceleme gerekmeksizin workflow hemen çalıştı ve npm token’ı sızdırıldı
Sızdırılan token ile 20 pakete kötü amaçlı sürümler dağıtıldı
Bunların çoğu yaygın kullanılmayan paketlerdi ama @ctrl/tinycolor haftada yaklaşık 2 milyon kez indirilen popüler bir paket
Hâlâ anlamadığım şey, angulartics2 deposundaki npm token’ı ile tinycolor’a nasıl publish yapılabildiği
Benim de başka birinin npm deposunda yönetici yetkim var ve son sürümlerin çoğunu da neredeyse ben çıkardım
Yönetici olduktan sonra, uzun süredir birikmiş sorunları bu vesileyle düzeltmek istediğim için commit’lerin çoğu da benim adıma görünmeye başladı
GitHub action ile paket publish etme fikrine neredeyse ikna olmuştum, ama 2FA ile elle dağıtım yaparken yanlışlıkla master dışı bir durumu publish etmekten hep korkuyordum
Bu yüzden diğer yöneticilerle bunu konuşmayı da ertelemiştim; sonunda böyle bir şey yaşanınca ertelemekte haklıymışım gibi hissediyorum
Doğru cevabın ne olduğunu bilmiyorum ama kimlik bilgilerini üçüncü taraflara emanet etmek kesinlikle iyi bir çözüm gibi durmuyor
Yeterince açık anlatmadıysam kusura bakmayın
Bu token, benim tüm npm paketlerim üzerinde global publish yetkisine sahip bir token’dı
Son 10 yıldır manuel release savunuyorum
Her zaman çok itiraz geldi ama artık o kadar da garip bir fikir gibi görünmüyor
CI/CD’nin havalı olduğunu biliyorum ama bu olay ya da son dönemdeki CF meseleleri, otomasyon yüzünden ciddi sorunların daha kolay ortaya çıkabildiğine dair giderek daha fazla kanıt sunuyor
Bir zamanlar BigBank’te çalışırken production dağıtımı için en az beş kişi hazır bekler, pek çok prosedür uygulanırdı; ama en azından neyin dağıtıldığını kesin olarak biliyorduk
Bence GitHub Actions ya da otomatik release script’leri yerine, eskisi gibi elle build alıp imzalayıp tarball yükleyip doğrulamak çok daha güvenli
Dağıtım sistemleri (örneğin Debian gibi distribution paketleme sistemleri) ayrıca bir doğrulama aşamasından da geçiyor; xz olayında tüm internetin ele geçirilmemesinin nedenlerinden biri de buydu
En azından release publish edilmeden önce bir insanın binary’yi bizzat imzalamasını zorunlu kılmak gerekir
Saldırganın kendini maintainer olarak ekleyip kendi anahtarıyla imzalaması da mümkün olduğundan, distribution paketleme sistemlerindeki gibi güvenilir anahtar yönetimi de eşlik etmeli ki daha güvenli olsun
Eğer tehdit modelim “tek bir GitHub hesabı ya da API anahtarı sızarsa bütün kullanıcı tabanı düşer” varsayımına dayanıyorsa, bunun gerçekten makul olup olmadığını yeniden düşünmem gerekir
Yayınlama için 2FA kullanmak iyi ama birden fazla yazarın kriptografik imza ile onay vermesi çok daha güvenli olur
Tek bir kişinin ele geçirilmesi saldırının başarıya ulaşmasına yetmemeli
Birçok paketin yazarı zaten yalnızca bir kişi
Birden fazla yazarın imzasını zorunlu kılmak iyi bir fikir, ama commit’lerde, tag’lerde ve çıktı artefact’larında herhangi bir biçimde imza doğrulaması yapılabilse bile saldırıların çoğu önlenebilir
Distribution paketleme sistemleri imza doğrulamasını çok iyi destekliyor ama dil paket yöneticilerinde bu doğrulama zinciri zayıf
Örneğin runc’ın resmi release süreci tamamen maintainer anahtarlarıyla imzalanıyor ve anahtarlar Yubikey gibi cihazlarda tutuluyor
Distribution sistemleri de ayrı keyring’ler yöneterek resmi kaynak ve binary’leri doğruluyor
Böyle bir süreç olsaydı bu saldırı da muhtemelen birden fazla aşamada engellenirdi
CI’da doğrudan build alınabilir, ama final aşamada maintainer’ın bizzat imzaladığı bir yapı gerekli
Dil paket yöneticilerinde böyle bir workflow yoksa Trusted Publishing nispeten daha az kötü bir alternatif sayılabilir
Ama GitHub hesabı ele geçirilirse (örneğin cookie hırsızlığıyla) anında publish etmek yine mümkün olur
GitHub, Trusted Publishing için timeout gibi güvenlik ayarları sunuyor ama saldırgan bunları kapatabilir
Benim hesabım ele geçirilse bile, distribution tarafı benim imzalamadığım bir anahtarla yapılan değişikliği kabul etmeyeceği için nispeten daha güvenli
Not: SUSE’de çalışıyorum ama openSUSE, Arch ve Gentoo gibi sistemlerde de artefact doğrulama desteğinin artmasını isterim
İlgili bağlantılar:
runc.keyring
keyring_validate.sh
release_sign.sh
openSUSE’nin runc.keyring’i
Token’lardan gerçekten nefret ediyorum
Token dediğiniz şey aslında statik bir paroladan farksız
Daha düzgün bir kimlik doğrulama yöntemi olması gerektiğini düşünüyorum
Örneğin GitHub’ı AWS için token sağlayıcısı olarak kullanmak, gördüğüm kadarıyla daha makul bir yöntem
GitHub-AWS OIDC entegrasyonu
Ama bu sadece istisnai bir örnek
Makineden makineye OIDC akışı doğru uygulanırsa güvenli olabilir, ama kurulumu fazla karmaşık
Ve sonuçta OIDC de bir bakıma sadece “daha karmaşık bir token” gibi hissettiriyor
İnsan denetimi olmayan otomasyon ortamlarında her zaman bir yerde sızdırılabilecek bir şey kalıyor; ister token olsun ister token üreteci
Bu worm örneğinde de OIDC temel çözüm değildi
GitHub workflow ele geçirilmişse, OIDC olsun olmasın ortama geçici bir kimlik enjekte edilmiş oluyor
Asıl önemli olan, yetkisiz kullanıcıların secret’lara sahip workflow’ları çalıştıramadığı bir sistem kurmak
İnce taneli yetki ayrımı isteniyorsa, OIDC yerine belki de token yetki kapsamını daraltmak daha etkili olabilir
Token’ın asıl fikri, ömrünün ve yetki kapsamının (authZ) sınırlı olmasıdır
Ama çoğu durumda pratikte böyle olmuyor ve parola gibi statik şekilde kullanılıyor
oauth ya da ayrıntılı yetki sınırlaması yapabilen biscuits gibi alternatifler var ama fiilen pek kullanılmıyorlar
Trusted Publishing artık npm dâhil çeşitli paket kayıtlarında destekleniyor
İlgili duyuru
Başkalarının da belirttiği gibi, token’lar yalnızca kısa ömürlü olmalı ya da ancak manuel kimlik doğrulamadan (MFA, passphrase vb.) sonra verilmeli
mTLS (TLS istemci sertifikası) kullanmak, doğru cevaba daha yakın bir yön gibi görünüyor
Güvenlik açığı olan npm paketlerini kontrol etmek için açık bir araç/script bilen var mı?
stepsecurity sayfasında böyle bir araç yok gibi görünüyor
Her şeyi engelleyemez ama provenance-action kullanmak da iyi bir fikir olabilir
provenance-action
Bilinen sorunlar için temel araç
npm auditBenim demek istediğim, yerel yayınlamada yanlış branch’ten çıkmak ya da build eksikliği gibi hatalardan biraz endişe duyduğumdu
Bir CI job’ın git geçmişinin derin kısımlarına force push ile değişiklik yapması durumunu da düşünmeden edemiyorum
Mevcut düzen artık düzgün çalışmıyor
Elbette OIDC token’ları, zero-trust çözümleri gibi teknik kazanımları övebiliriz
Ama milyonlarca indirme alan npm kütüphanelerinin bakımcılarının önemli bir kısmı, gerçekten hacklenene ya da npm doğrudan dağıtımı engelleyene kadar güvenliği ciddiye almayacak
Ve sonra “tüm bağımlılıkları kaldıralım, sadece standart kütüphane kalsın” gibi uygulanamaz öneriler geliyor
Bağımlılıkları azaltmak iyi bir şey ama mevcut soruna hiçbir çözüm getirmiyor
Gerçekçi seçenekler ya on binlerce, yüz binlerce kişinin npm’i bırakıp kodlarını baştan yazması, ya da npm’in çok indirilen paketlerden başlayarak 2FA, OIDC gibi kuralları zorunlu kılması ve uymayanların publish etmesini tamamen engellemesi
Hangi seçeneğin pratikte daha uygulanabilir olduğu çok açık
Aksi hâlde npm’in itibarı dibe vurur ve ortaya XKCD 927 durumu çıkar