Blog'a Dön

AsyncAPI npm Saldırısı: Provenance Commit'i Değil, Pipeline'ı Kanıtlar

Zehirli @asyncapi paketleri, geçerli provenance ile meşru bir trusted publishing pipeline'ından yayınlandı ve import anında çalıştığı için --ignore-scripts'i aştı. Bu hafta neleri kontrol etmelisiniz?

Umut Korkmaz

Yayın tarihi: 4 dk okuma

14 Temmuz'da @asyncapi npm scope'undaki dört paketin zehirli sürümleri yayınlandı. Üstelik bu sürümler projenin kendi release pipeline'ları üzerinden çıktı. 15 Temmuz'da Microsoft AsyncAPI saldırısının ayrıntılı bir analizini yayınladı. JavaScript paketi yayınlayan ya da kullanan herkesi iki ayrıntı ilgilendiriyor: Paketlerin provenance bilgisi geçerliydi ve payload modül import edildiğinde çalışıyor. Bu yüzden --ignore-scripts onu durdurmak için hiçbir şey yapmıyor.

Neler yayınlandı

Microsoft'a göre dört paket adı altında beş sürüm yayınlandı:

  • @asyncapi/specs 6.11.2-alpha.1 ve 6.11.2
  • @asyncapi/generator 3.3.1
  • @asyncapi/generator-components 0.7.1
  • @asyncapi/generator-helpers 1.1.1

Microsoft üç generator paketinin yaklaşık 07:10 UTC'de yayınlandığını belirtiyor. npm registry'sinde ise specs alpha sürümü 08:06 UTC, 6.11.2 sürümü 08:30 UTC olarak görünüyor. Microsoft'un işaret ettiği temiz sürümler şunlar: specs 6.11.1 veya öncesi, generator 3.3.0, generator-components 0.7.0 ve generator-helpers 1.1.0.

Bir preview workflow'u nasıl release anahtarına dönüştü

Giriş noktası asyncapi/generator reposundaki manual-netlify-preview.yml adlı GitHub Actions workflow'uydu. Bu workflow pull_request_target ile çalışıyordu. Bu da job'ı base reponun güvenlik bağlamına yerleştiriyordu. Buna rağmen pull request'in güvenilmeyen head commit'ini checkout ediyordu. Checkout credential'ları, job sonrası temizliğe kadar yerel Git config'inde kalıyordu ve job'da repository secret'larına referans veren adımlar vardı. Saldırgan, içeriği uzaktaki bir JavaScript kodunu indirip çalıştıracak şekilde tasarlanmış bir pull request açtı. Microsoft bunun asyncapi-bot personal access token'ını açığa çıkardığını söylüyor, ancak token'ın tam olarak nasıl elde edildiğinin public loglarda görünmediğini de not ediyor. Sonraki push'lar asyncapi-bot olarak kimlik doğrulandı.

Push'lar auto-publish branch'lerine gitti. Yani npm'i ele geçirmeye hiç gerek yoktu: Commit'ler gerçek release workflow'larını tetikledi ve bu workflow'lar GitHub OIDC ile npm trusted publishing üzerinden yayın yaptı. Microsoft, tetikleyen commit'ler yetkisiz olmasına rağmen provenance attestation'larının geçerli olduğunu ve meşru repoları, commit'leri ve workflow'ları doğru şekilde gösterdiğini belirtiyor.

İşin rahatsız edici kısmı da bu. Provenance "bunu hangi pipeline build etti?" sorusunu yanıtlar. "Bu pipeline bu commit üzerinde çalışmalı mıydı?" sorusunu yanıtlamaz. Bir saldırgan, release workflow'unun izlediği branch'e push yapabildiği anda OIDC publishing, zararlı bir build'i aklamanın son derece inandırıcı bir yoluna dönüşür.

--ignore-scripts neden işe yaramadı

npm olaylarında olağan tavsiye --ignore-scripts kullanmaktır, çünkü klasik saldırı bir postinstall hook'unda yaşar. Microsoft bunu daha yaygın desen olarak tanımlıyor ve bu kampanya o yolu kullanmadı: Paketlerde hiçbir install hook tanımlı değildi. Loader her paketin giriş yolunda duruyor (@asyncapi/specs için index.js). npm dokümantasyonu ignore-scripts seçeneğini, npm'in package.json dosyalarında tanımlı script'leri çalıştırmasını engelleyen bir ayar olarak tanımlıyor; require veya import sırasında çalışan kod üzerinde hiçbir etkisi yok. Microsoft açık konuşuyor: Burada ona güvenmeyin.

Import anında enjekte edilen blok, gizli ve ayrık (detached) bir Node child process başlatıyor. Bu process IPFS'ten yaklaşık 8,2 MB'lık şifreli bir paket indiriyor ve bu ikinci aşama, Microsoft'un Miasma adını verdiği bir runtime'ı çözüyor. Analiz ettikleri build'de persistence ve C2 aktifti; credential toplama modülü ise mevcut ama devre dışıydı. Hedef listesi yine de okumaya değer: GITHUB_TOKEN, NPM_TOKEN, cloud anahtarları, VAULT_TOKEN, .npmrc, SSH anahtarları, kubeconfig ve ayrıca ANTHROPIC_API_KEY ile OPENAI_API_KEY. Ben her gün birkaç coding agent'ı paralel çalıştırıyorum ve böyle kurulmuş bir makinenin ortam değişkenlerinde genellikle tam olarak bu anahtarlar bulunur.

Persistence işletim sistemine göre değişiyor: Windows'ta bir HKCU Run anahtarı, Linux'ta bir systemd user unit, macOS'ta ise .zshrc, .bashrc veya .bash_profile dosyalarına shell RC enjeksiyonu.

Pazartesi neleri kontrol ederdim

Bu paketleri kullanıyorsanız:

  • Lockfile'larda bu beş sürümü arayın ve temiz sürümlere pin'leyin.
  • Geliştirici makinelerinde ve build host'larında npm ve Yarn cache'lerini temizleyin ki cache'lenmiş bir tarball geri gelmesin.
  • Herhangi bir şey kötü bir sürümü import ettiyse, credential'ları temiz bir host'tan rotate edin. Microsoft'un risk altındakiler listesi geliştirici iş istasyonlarını, CI/CD pipeline'larını, container build'lerini ve production servislerini kapsıyor.
  • 85.137.53[.]71 adresine 8080, 8081 ve 8091 portları üzerinden yapılan bağlantılar için alarm kurun ve public IPFS gateway'lerini perimeter'da engellemeyi düşünün.
  • npm CLI 11.10.0 ile eklenen ve Microsoft'un önerdiği min-release-age ayarına göz atın. Yalnızca belirli bir gün sayısından daha uzun süredir yayında olan sürümleri kurar; böylece birkaç saat önce yayınlanmış bir sürüm seçilmez.

Paket yayınlıyorsanız:

grep -rn "pull_request_target" .github/workflows/
  • pull_request_target ile PR head'ini checkout etmeyi birleştiren her job'ı, aksi kanıtlanana kadar bir güvenlik açığı olarak kabul edin.
  • Uzun ömürlü bot PAT'lerini daha dar kapsamlı ve daha kısa ömürlü credential'larla değiştirin; güvenilmeyen koda dokunan job'larda checkout credential'larını kalıcı bırakmayın.
  • Publish job'ını onay gerektiren korumalı bir environment'ın arkasına koyun ki tek başına bir push release çıkaramasın. Microsoft gözden geçirilmesi gereken alanlar olarak token scope'larını, workflow onaylarını, korumalı environment'ları, release provenance'ını ve otomatik yayın etrafındaki anomali tespitini sıralıyor.

Benim görüşüm

Trusted publishing, bir repository secret'ında duran uzun ömürlü bir npm token'ından hâlâ daha iyi ve kimseye onu kapatmasını söylemem. Ama bu olay, trusted publishing'in güven sınırını ortadan kaldırmadığını, sadece yerini değiştirdiğini gösteriyor. Soru artık "npm token'ı kimde?" olmaktan çıkıp "release workflow'unu kim çalıştırabilir?" haline geliyor. Bir lead için bunun anlamı şu: Workflow dosyaları production kodu kadar ciddi review'u hak ediyor ve bir release, doğru branch'teki bir commit'le değil, bir insan kararıyla çıkmalı. Provenance size bir paketin nereden geldiğini söyler. O paketin var olması gerekip gerekmediğini söylemez.

Kaynaklar

Bu yazıda yararlanılan birincil kaynaklar.

  1. microsoft.com/en-us/security/blog/2026/07/15/unpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery (yeni sekmede açılır)
  2. registry.npmjs.org/@asyncapi%2fspecs (yeni sekmede açılır)
  3. docs.npmjs.com/cli/v11/using-npm/config (yeni sekmede açılır)
  4. github.com/npm/cli/releases/tag/v11.10.0 (yeni sekmede açılır)

Yazar hakkında

Umut Korkmaz

Lead Yazılım Mühendisi

İstanbul'da kıdemli yazılım mühendisi olarak çalışıyorum. Burada, bizzat canlıya aldığım araçları ve sürümleri, sahada öğrendiklerimle birlikte yazıyorum.

Ortak konulara göre seçildi.