Bayangkan skenario ini di kantor Anda: seorang developer menjalankan npm install pada proyek Next.js internal pada pagi hari. Tidak ada peringatan, tidak ada error, aplikasi berjalan normal. Namun dalam hitungan detik, kredensial AWS, token GitHub, token Kubernetes, dan seluruh isi variabel lingkungan CI/CD perusahaan sudah dikirim keluar dalam bentuk terenkripsi ke repositori milik penyerang. Yang lebih meresahkan: paket yang mereka pasang membawa tanda tangan provenance yang sah dan lolos semua verifikasi kriptografis.
Itu bukan skenario hipotetis. Itu yang terjadi pada 4 Agustus 2026, ketika paket npm keyv dan keluarga cacheable dibajak dalam serangan rantai pasok yang oleh Cyber Security Agency of Singapore (CSA) disebut sebagai varian ChainDrop dari keluarga malware Shai-Hulud. Menurut advisory CSA tanggal 6 Agustus 2026, kampanye ini mengompromikan lebih dari 1.300 versi paket dengan gabungan sekitar 2 miliar unduhan per bulan.
Bagi ekosistem developer Indonesia yang sangat bergantung pada Node.js untuk membangun aplikasi web, dashboard pemerintahan, hingga backend layanan keuangan, insiden ini layak dibedah baris demi baris. Artikel ini menguraikan cara kerja teknis serangan tersebut dan langkah pengerasan yang bisa diterapkan tim rekayasa hari ini.
Mengapa keyv dan cacheable Menjadi Sasaran Ideal
keyv adalah lapisan abstraksi penyimpanan key-value dengan adapter untuk Redis, SQLite, Postgres, dan MongoDB. Sementara cacheable, cacheable-request, flat-cache, dan file-entry-cache adalah pustaka caching dari ekosistem maintainer yang sama.
Kuncinya: hampir tidak ada developer yang memasang paket-paket ini secara langsung. Mereka berada jauh di dalam pohon dependensi. Rantai yang paling umum adalah eslint → file-entry-cache → flat-cache → keyv. Artinya, siapa pun yang memasang linter standar di proyek JavaScript berpotensi terpapar tanpa pernah menyebut nama keyv di package.json mereka.
Semakin dalam sebuah paket berada di pohon dependensi, semakin besar jangkauannya dan semakin kecil kemungkinan ada orang yang memeriksanya.
Titik Masuk: Akun Maintainer, Bukan Kerentanan Kode
Tidak ada CVE dalam insiden ini. Tidak ada bug memori, tidak ada injeksi SQL. Bukti menunjukkan akun maintainer dibajak, lalu digunakan untuk mempublikasikan rilis berbahaya di dua keluarga paket sekaligus.
Berdasarkan timestamp registri npm (semua waktu UTC, 4 Agustus 2026):
- Pukul 09:30–09:32 — paket bercakupan
@keyv/*(misalnya@keyv/redis,@keyv/sqlite) merilis tarball versi 6.0.0. - Pukul 09:35 —
keyv@6.0.0dipublikasikan; ini versi pertama yang membawa preinstall hook berbahaya. - Pukul 09:38 — sebuah paket di luar kedua namespace ikut terpublikasi, tanda kampanye sudah menjangkau akun lain.
- Sekitar pukul 09:39 — repositori sumber menunjukkan force push ke
main, penghapusan tag berulang, dan commit berjudul penambahansetup.mjssertaMath_Symbol.js. Pelaku masih memegang kendali akun dan CI saat itu. - Pukul 10:09–10:14 — keluarga
cacheableterpublikasi dalam satu ledakan:cacheable@2.5.1,flat-cache@6.1.24,cacheable-request@13.0.20,file-entry-cache@11.1.6,cache-manager@7.2.10, dan lainnya.
Seluruh eskalasi berlangsung dalam waktu kurang dari satu jam. Kecepatan inilah yang membuat model pertahanan berbasis “tunggu advisory, lalu tambal” tidak lagi memadai.
Anatomi Payload: Dua Tahap yang Rapi
Yang Tidak Diubah Sama Sekali
Detail paling elegan sekaligus paling berbahaya: kode pustaka yang dipublikasikan — isi direktori dist/ — identik byte demi byte (SHA-256) dengan build bersih 6.0.0-rc.1. Satu-satunya perubahan pada paket adalah package.json ditambah dua berkas baru. Aplikasi Anda akan berperilaku normal sepenuhnya, sementara host sudah dikuasai.
"files": [ "dist", "LICENSE", "setup.mjs", "Math_Symbol.js" ],
"scripts": {
"preinstall": "node setup.mjs"
}
Tahap 1 — Loader Bun (setup.mjs)
setup.mjs adalah skrip Node yang diobfuskasi ringan. Jika runtime bun belum ada di sistem, skrip ini mengunduh biner Bun standalone versi 1.3.13 langsung dari halaman rilis GitHub, lalu memakainya untuk mengeksekusi tahap kedua. Loader mendeteksi platform dan arsitektur, termasuk lingkungan Alpine dan musl melalui ldd --version dan /etc/os-release.
Ada dua implikasi defensif yang penting di sini. Pertama, biner Bun diunduh melalui HTTPS tanpa verifikasi checksum atau tanda tangan. Kedua, karena payload dieksekusi oleh Bun dan bukan Node, kontrol keamanan yang hanya memantau proses node berpotensi terlewat sepenuhnya. Direktori temporer bun-dl-* juga dibersihkan setelah eksekusi untuk meminimalkan artefak di disk.
Tahap 2 — Payload (Math_Symbol.js)
Tahap kedua adalah bundel Bun berukuran sekitar 728 KB. String di dalamnya dilindungi dengan polymorphic basE91 encoding: satu tabel opcode numerik menggerakkan puluhan alfabet berbeda per lingkup yang didekode secara lazy. Tag log internal mengungkap arsitektur modularnya: [collector], [dispatcher], [provenance], dan [publish].
Modul collector menyisir rahasia dengan cakupan yang sangat luas:
- Metadata cloud — kueri aktif ke Instance Metadata Service AWS (
169.254.169.254) dan endpoint kredensial kontainer (169.254.170.2), termasuk rantai kredensial AWS dan Secrets Manager lintas region. - Kunci penyedia lain — private key service account GCP dan client secret Azure sebagai target regex dan berkas.
- Rahasia infrastruktur — token HashiCorp Vault dari jalur seperti
/home/runner/.vault-tokendan/run/secrets/VAULT_TOKEN, serta token service account Kubernetes dari/var/run/secrets/kubernetes.io/serviceaccount/token. - Identitas developer — token npm melalui endpoint
whoamidan token registri, ditambah metadata secret organisasi dan repositori GitHub Actions. - Sisiran generik — pemindaian ala TruffleHog untuk kunci umum, bearer token, dan blok private key di disk.
Dari Pencurian Kredensial Menjadi Worm
Inilah yang mengubah insiden ini dari pembobolan menjadi wabah. Setelah token npm korban dikantongi, payload menjalankan siklus replikasi mandiri: ia mencari paket lain milik maintainer yang sama melalui endpoint pencarian registri (?text=maintainer:), menukar identitas menjadi kredensial publikasi via mekanisme OIDC trusted publishing, mengunduh tarball target, menyuntikkan preinstall hook yang sama, menghitung ulang field integrity dan shasum, menaikkan nomor versi, lalu melakukan PUT ke registri.
Satu token CI yang bocor cukup untuk memperluas kampanye ke seluruh portofolio paket korban berikutnya.
Pelajaran Terpenting: Provenance Menjamin Build, Bukan Sumber
Selama dua tahun terakhir, komunitas open source mendorong build provenance — attestation DSSE, sertifikat Fulcio, entri transparency log Rekor — sebagai jawaban atas serangan rantai pasok. Insiden ini menunjukkan batas tegas dari pendekatan tersebut.
keyv@6.0.0 dikirim dengan attestation yang lolos verifikasi. Bukan karena kriptografinya gagal, melainkan karena alur rilis yang sah membangun dari source yang sudah lebih dulu ditrojanisasi. Pipeline npm dan sigstore melakukan tepat apa yang dirancang untuk dilakukan: menandatangani apa pun yang keluar dari build resmi. Terpisah dari itu, payload juga membawa komponen provenance sendiri yang mampu menerbitkan attestation baru untuk paket yang ia republikasi.
Provenance membuktikan “artefak ini benar dibangun oleh pipeline X”. Ia tidak pernah membuktikan “kode di dalam pipeline X tidak jahat”. Verifikasi tanda tangan adalah kondisi perlu, bukan kondisi cukup.
Eksfiltrasi dan Persistensi Tanpa C2 Tunggal
Payload menghindari satu titik command and control yang mudah diblokir. Komponen pengirim membuat repositori GitHub baru melalui POST /user/repos dan menitipkan temuan lewat mutasi GraphQL createCommitOnBranch. Komponen lain meresolusi tujuan pengiriman via DNS dan melakukan health check sebelum mengirim. Semua data dikirim sebagai ciphertext AES-256-GCM (IV 12 byte, tag 16 byte), sehingga repositori dan endpoint penerima hanya melihat data terenkripsi.
Yang paling relevan untuk tim modern: repositori sumber juga ditanami autostart hook di .claude/settings.json (hook SessionStart) dan .vscode/tasks.json (task folderOpen). Keduanya memicu loader yang sama begitu seorang developer — atau agen AI coding — membuka repositori hasil kloning. Tanpa perlu menjalankan npm install sama sekali.
Dead-Man’s Switch: Jangan Rotasi Kredensial Lebih Dulu
Ini bagian yang wajib dipahami tim respons insiden. Payload memasang mekanisme pemantau di level host: token GitHub curian dan sebuah perintah handler ditulis ke ~/.config/gh-token-monitor/ dengan mode 600, lalu dipersistenkan sebagai LaunchAgent macOS (com.user.gh-token-monitor, dengan RunAtLoad dan KeepAlive) atau service pengguna systemd di Linux dengan loginctl enable-linger agar bertahan setelah logout.
Skrip pengawas di ~/.local/bin/gh-token-monitor.sh memanggil API GitHub setiap 60 detik. Saat token berhenti berfungsi — yaitu tepat pada momen token dicabut atau dirotasi — skrip mengeksekusi string handler yang dipasok dari jarak jauh, lalu menghapus jejaknya sendiri.
if [[ "$HTTP_STATUS" =~ ^40[0-9]$ ]]; then
eval "$HANDLER" # dipicu oleh pencabutan / rotasi token
rm -f "$STARTED_FILE"; exit 0
fi
Unit systemd-nya bahkan menamai diri sebagai “GitHub Token Validity Monitor” sehingga terlihat seperti utilitas developer biasa. Konsekuensi operasionalnya jelas: rotasi kredensial yang dilakukan sebelum switch ini dibersihkan justru menjadi pemicu payload berikutnya. Urutan penanganan menjadi krusial.
Konteks Indonesia: Permukaan Serangan yang Melebar Cepat
Insiden ini datang di tengah tren yang mengkhawatirkan. Laporan Ancaman Digital di Indonesia Semester I 2026 dari platform AwanPintar.id mencatat 257.976.333 serangan pada Januari–Juni 2026 — setara sekitar 16 serangan per detik, naik sekitar 93,33 persen dibanding 133.439.209 serangan pada semester I 2025.
Yang paling selaras dengan pembahasan kita adalah pergeseran jenis serangan. Porsi Attempted Administrator Privilege Gain melonjak dari 2,76 persen menjadi 43,65 persen, sementara Attempted Information Leak naik dari 4,66 persen menjadi 13,97 persen. Dengan kata lain: penyerang di Indonesia semakin fokus mengejar hak akses istimewa dan mengeluarkan data — dua hal yang tepat menjadi tujuan worm seperti ChainDrop.
Dari sisi kepatuhan, jika kredensial yang bocor membuka jalan ke basis data pelanggan, kewajiban hukum langsung berlaku. UU No. 27 Tahun 2022 tentang Pelindungan Data Pribadi mewajibkan pengendali data menyampaikan pemberitahuan tertulis kepada subjek data dan lembaga terkait paling lambat 3x24 jam sejak kegagalan pelindungan data pribadi diketahui. Artinya, kemampuan tim Anda menjawab “kredensial mana yang terpapar, dan sistem apa yang bisa diaksesnya” dalam hitungan jam adalah persoalan kepatuhan, bukan sekadar teknis.
Langkah Pengerasan yang Bisa Diterapkan Pekan Ini
1. Matikan Eksekusi Script Instalasi secara Default
Seluruh rantai serangan ini bergantung pada lifecycle script. Menonaktifkannya memotong Tahap 1 sebelum berjalan.
# .npmrc di root proyek dan di image CI
ignore-scripts=true
audit=true
fund=false
# Instalasi deterministik dari lockfile, bukan resolusi ulang
npm ci --ignore-scripts
# Verifikasi tanda tangan registri (perlu, tapi tidak cukup)
npm audit signatures
Jika ada dependensi yang benar-benar butuh build step native, jalankan secara eksplisit dan terbatas untuk paket tersebut, bukan membuka izin global.
2. Kunci Versi secara Ketat dan Tunda Rilis Baru
Hindari rentang caret dan tilde untuk dependensi kritis. Kunci berdasarkan versi eksak dan integrity hash. Terapkan jeda karantina — misalnya 24–72 jam — sebelum versi baru diizinkan masuk ke build produksi, karena artefak berbahaya dalam kampanye ini terdeteksi rata-rata hanya sekitar lima menit setelah publikasi, dan mayoritas ditarik dalam hitungan jam.
3. Perlakukan CI Runner sebagai Zona Tidak Dipercaya
- Jangan menaruh kredensial cloud jangka panjang di runner; gunakan OIDC berumur pendek dengan cakupan minimum.
- Blokir akses runner ke endpoint metadata instance (
169.254.169.254) bila tidak dibutuhkan. - Batasi egress runner ke daftar domain yang diizinkan — worm ini mengandalkan unduhan runtime pihak ketiga dan pengiriman data ke tujuan hasil resolusi DNS.
- Gunakan runner sekali pakai (ephemeral) yang dihancurkan setelah setiap job.
4. Buru Indikator Kompromi, dalam Urutan yang Benar
Untuk lingkungan yang memasang versi terdampak dan menjalankan install script, perlakukan host sebagai terkompromi. Bersihkan dead-man’s switch dan implan lebih dulu, baru rotasi kredensial.
# 1) Periksa implan persistensi (JANGAN rotasi token dulu)
ls -la ~/.local/bin/gh-token-monitor.sh 2>/dev/null
ls -la ~/.config/gh-token-monitor/ 2>/dev/null
ls -la ~/Library/LaunchAgents/com.user.gh-token-monitor.plist 2>/dev/null
systemctl --user status gh-token-monitor.service 2>/dev/null
# 2) Periksa artefak loader dan payload
find . -name "setup.mjs" -o -name "Math_Symbol.js" -o -name "math_init.js"
find /tmp -maxdepth 1 -name "bun-dl-*"
# 3) Periksa autostart hook di repositori hasil kloning
cat .claude/settings.json 2>/dev/null
cat .vscode/tasks.json 2>/dev/null
# 4) Periksa versi terdampak di pohon dependensi
npm ls keyv flat-cache file-entry-cache cacheable cacheable-request cache-manager
Versi yang tercatat terdampak dalam advisory CSA antara lain keyv@6.0.0, flat-cache@6.1.24, file-entry-cache@11.1.6, cacheable-request@13.0.20, cacheable@2.5.1, @cacheable/memory@2.2.1, cache-manager@7.2.10, @cacheable/node-cache@3.1.2, @cacheable/utils@2.5.1, @cacheable/net@2.1.1, dan ecto@5.0.1. Indikator jaringan yang dipublikasikan mencakup domain npm-cache[.]com, js-mirror[.]com, dan pypi-get[.]com.
Setelah implan bersih, cabut — bukan hanya rotasi — token npm dan GitHub, lalu rotasi kunci AWS/GCP/Azure, token Vault, token service account Kubernetes, serta seluruh secret organisasi dan repositori di CI. Audit akun npm untuk versi paket yang tidak Anda terbitkan, dan audit GitHub untuk repositori baru serta commit tak dikenal.
5. Bangun Kerangka, Bukan Sekadar Tambalan
Jadikan ini momentum menata praktik rekayasa. NIST SP 800-218 (Secure Software Development Framework) dan NIST SP 800-161r1 untuk manajemen risiko rantai pasok siber memberi struktur yang matang, sementara ISO/IEC 27001:2022 menuntut kontrol atas keamanan dalam relasi pemasok. Praktik konkret yang paling berdampak: menyusun dan memelihara SBOM agar Anda bisa menjawab pertanyaan “apakah kami memakai paket ini” dalam menit, bukan hari; memakai proxy registri internal dengan allowlist; dan menjalankan pemindaian perilaku dependensi, bukan hanya pencocokan CVE — sebab dalam insiden ini, tidak ada satu pun CVE yang bisa dipindai.
Penutup
Serangan pada keyv dan cacheable adalah pengingat bahwa pertahanan rantai pasok software tidak bisa disandarkan pada satu kontrol tunggal. Tanda tangan yang valid, provenance yang terverifikasi, dan maintainer yang bereputasi baik semuanya hadir dalam insiden ini — dan semuanya tetap lolos, karena yang dikompromikan adalah manusia dan akunnya, bukan matematika kriptografinya.
Yang membedakan organisasi yang pulih dalam sehari dari yang tersandera berminggu-minggu bukanlah keberuntungan, melainkan tiga hal yang bisa dipersiapkan sekarang: visibilitas atas dependensi (SBOM yang hidup), pembatasan blast radius (kredensial berumur pendek dan runner sekali pakai), dan prosedur respons yang benar urutannya. Di ekosistem tempat satu npm install bisa memanggil ratusan paket dari puluhan maintainer yang tidak pernah Anda kenal, asumsi dasarnya harus berubah: setiap dependensi adalah kode pihak ketiga yang berjalan dengan hak akses penuh developer Anda.
Siberindo membantu organisasi Indonesia melakukan penilaian keamanan rantai pasok software, penyusunan SBOM, pengerasan pipeline CI/CD, serta respons insiden dan forensik digital saat kompromi kredensial terjadi.


