【緊急】npmサプライチェーン攻撃でkeyvが汚染 — Shai-Huludワームの手口と今すぐやるべき対処法

約19分で読めます by ぽんたぬき
【緊急】npmサプライチェーン攻撃でkeyvが汚染 — Shai-Huludワームの手口と今すぐやるべき対処法

【緊急】npmサプライチェーン攻撃でkeyvが汚染 — Shai-Huludワームの手口と今すぐやるべき対処法

あなたのプロジェクトの package-lock.json、今すぐ確認してください。

2026年8月4日 09:35 UTC、npmパッケージ管理者アカウントの乗っ取りを起点とした大規模なサプライチェーン攻撃が発生しました。わずか約30分で800以上のパッケージ・1,381バージョンに悪性コードが注入されるという、史上最大規模のnpm汚染事件です。「自分は keyv なんて使っていない」と思った方も油断は禁物です。ESLintを使っているだけで、あなたのプロジェクトは影響圏内に入っている可能性があります。

この記事を読めば、①自分のプロジェクトが危険かどうかを判断でき、②今すぐ実行すべき対処法がわかります。


3行でわかる今回の事件

  • npmの著名パッケージ管理者アカウントが乗っ取られ、keyv など800以上のパッケージに**自己伝播型ワーム「Shai-Hulud」**が注入された
  • 感染すると GitHub / npm / AWS / SSH などの認証情報が窃取され、さらに被害者が管理する別パッケージへ自動伝播する
  • 対処は「①該当バージョンの確認 → ②クレデンシャル全ローテーション → ③--ignore-scripts の標準化」の3ステップ

該当バージョンかを30秒で確認するコマンド

# npmの場合
npm ls keyv 2>/dev/null | grep '6.0.0'

# ロックファイルを直接検索
grep -r '"keyv".*"6.0.0"' package-lock.json pnpm-lock.yaml yarn.lock

上記コマンドで結果が返ってきた場合は、本記事の「今すぐやるべき対処法」セクションへ直行してください。


何が起きたのか — 事件の全体像

発生日時と攻撃の起点

2026年8月4日 09:35 UTC、npmパッケージ管理者として著名な jaredwray 氏のGitHubアカウントが何者かに乗っ取られました。同氏は keyvflat-cachecacheable-request など、JavaScriptエコシステムを支える多数の人気パッケージのメンテナーです。

攻撃者はこの権限を悪用し、約30分という短時間で 800以上のパッケージ / 1,381バージョン に悪性コードを注入しました。この拡大速度が通常の単発汚染と根本的に異なる点については、後ほど詳しく解説します。

影響を受けた主要パッケージ一覧

パッケージ名 月間ダウンロード数 主な利用文脈
keyv 6.2億回 汎用キーバリューストア
flat-cache 5.8億回 ESLintの依存関係
file-entry-cache 5.7億回 ESLintの依存関係
cacheable-request 3,400万回 HTTPキャッシュ
cache-manager キャッシュ抽象化
@crawlee/* 系列 クローラーフレームワーク

なぜ「全JSエンジニア必読」なのか — 間接依存の恐怖

ここで重要なのが、flat-cachefile-entry-cache の立ち位置です。この2つは ESLintの依存関係 であり、多くのJavaScriptプロジェクトが直接・間接を問わず組み込んでいます。

あなたのプロジェクト
  └── eslint
        └── file-entry-cache
              └── flat-cache
                    └── keyv ← 汚染パッケージ

つまり、keyv を直接インストールした覚えがなくても、依存ツリーの深部に潜んでいる可能性があります。フロントエンドもバックエンドも関係なく、Node.jsを使うエンジニア全員が当事者です。


攻撃の技術的な仕組み — 3ステージ構造の「Shai-Huludワーム」

今回の攻撃が特に危険なのは、その技術的な精巧さにあります。「Shai-Hulud(砂漠の巨大ワーム)」と命名された悪性コードは、3つのステージに分かれた構造を持ちます。

Stage 0 — preinstall フックという侵入口

悪性バージョンの package.json には、以下の一行が追加されていました。

{
  "scripts": {
    "preinstall": "node setup.mjs"
  }
}

npmのライフサイクルフックは、npm install 実行と同時に任意のコードを自動実行できる設計になっています。つまり、「インストールしただけ」で侵害が成立します。ユーザー側に落ち度は一切ありません。

Stage 1 — ローダー setup.mjs(29,918バイト)

最初に実行されるローダーは、次のような高度な設計になっています。

  • RC4 + Base64による難読化: 静的スキャンによる検出を回避
  • OS・アーキテクチャの自動判別: Windows / macOS / Linux、x64 / arm64 など環境を自動検出して適切なペイロードを選択
  • Bunランタイムの動的ダウンロード: C2サーバー(npm-cache.com)からBunランタイムをリアルタイムでダウンロードして実行

最後の点が特に巧妙です。パッケージ本体にバイナリを含まないため、VirusTotalのような静的解析ツールではほぼ検出できません。インターネットへの外部通信が発生して初めて、その存在が明らかになります。

Stage 2 — メインペイロード Math_Symbol.js(728KB)

ローダーが起動する728KBのメインペイロードは、以下のクレデンシャルを網羅的に窃取します。

  • GitHub トークン / npm トークン — これが自己伝播の武器になる
  • AWS IAM キー — クラウドインフラへの侵害につながる
  • Kubernetes ServiceAccount トークン — コンテナ環境への侵害
  • SSH 秘密鍵 — サーバーへの直接侵入
  • 暗号通貨ウォレット秘密鍵 — 金銭的な直接被害

これらはすべてC2サーバー npm-cache.com(正規のnpmミラーと見まがうような命名)へ送信されます。

自己伝播 — なぜ30分で800パッケージに広がったのか

今回の攻撃が「ワーム」と呼ばれる理由がここにあります。窃取したnpmトークンを使い、被害者が管理する別のパッケージにも自動的に感染コードを注入します。

攻撃者が jaredwray アカウントを乗っ取る
  → keyv に悪性コードを注入
    → keyv をインストールした開発者Aのnpmトークンを窃取
      → 開発者Aが管理するパッケージ群にも悪性コードを注入
        → さらに連鎖...(30分で800パッケージ超)

従来の「1パッケージを汚染する」という手口と比べると、攻撃の規模と速度が根本的に異なります。これは量的な差ではなく、質的な脅威モデルの進化です。


最も厄介な点 — 正規のProvenance証明が付いていた

技術的に精巧なだけでなく、今回の攻撃には検証機構そのものを無効化するという要素も含まれていました。

OIDC信頼済みパブリッシャーの悪用

攻撃者はGitHub ActionsのOIDC信頼済みパブリッシャーワークフロー経由でパッケージを公開しました。その結果、汚染されたパッケージに正規のSLSAプロベナンス証明が付与されてしまいました。

「Provenanceが確認できるから安全」という前提が崩れた瞬間です。現在多くのセキュリティチームがProvenance検証を「サプライチェーン攻撃への対策」として導入していますが、今回の事例はその前提を根底から覆しています。

Ethereumスマートコントラクトによる C2 インフラ管理

さらに驚くべきことに、C2サーバーのアドレスをEthereumスマートコントラクト0xE1f2395ee43e45A1556EC6438a88c31B83493103)で管理する設計になっていました。

これにより、ペイロード本体を変更せずに攻撃インフラを切り替えることができます。セキュリティ機関がC2ドメインをテイクダウンしても、スマートコントラクトを書き換えるだけで別のインフラに移行できるため、テイクダウン耐性が著しく高い設計です。ブロックチェーン技術が攻撃インフラとして転用される、新しい潮流といえるでしょう。

教訓 — 単一の検証レイヤーに依存しない

今回の事件が示すのは、「Provenance検証は必要条件であって十分条件ではない」という事実です。単一の検証レイヤーへの過信は危険であり、多層防御(Defense in Depth)の発想をサプライチェーン管理にも適用する必要があります。


自分のプロジェクトが被害を受けたか確認する方法(IoC)

侵害の痕跡(Indicators of Compromise)

まず以下のIoCを使って、自社環境に痕跡がないか確認してください。

C2ドメイン: npm-cache.com プロキシログ、ファイアウォールログ、DNS解決ログを検索してください。

ファイルハッシュ(SHA256):

  • setup.mjs: 54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
  • Math_Symbol.js: 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc

ロックファイルを検査するコマンド集

# npm — インストール済み依存関係ツリーで確認
npm ls keyv 2>/dev/null | grep '6.0.0'

# ロックファイルから直接検索(npm / pnpm / yarn 共通)
grep -r '"keyv".*"6.0.0"' package-lock.json pnpm-lock.yaml yarn.lock

# モノレポで一括チェック
find . -name "package-lock.json" -not -path "*/node_modules/*" \
  -exec grep -l '"keyv".*"6.0.0"' {} \;

# flat-cache / file-entry-cache も同様に確認
grep -r '"flat-cache"' package-lock.json | grep '"6\.'

「ローカルでは使っていないが、CIは回っていた」場合の考え方

ローカル環境がクリーンでも油断は禁物です。CI/CDランナー・Dockerビルドキャッシュも調査対象に含めてください。感染時刻(2026年8月4日 09:35 UTC = 日本時間 18:35 JST)以降に実行されたビルドジョブのログを重点的に確認しましょう。


今すぐやるべき対処法

STEP 1 — クレデンシャルの即時ローテーション(最優先)

感染の可能性がある場合、最初にやるべきことはクレデンシャルのローテーションです。以下を対象に、今すぐ実施してください。

対象 ローテーション手順
npm トークン npmjs.com のアカウント設定から全トークンを無効化・再生成
GitHub トークン Settings > Developer settings > Personal access tokens から全削除
AWS IAM キー IAM コンソールからキーを無効化・削除、新規キーを発行
SSH 秘密鍵 ~/.ssh/ 以下の全秘密鍵を再生成し、公開鍵を各サーバーに再登録
Kubernetes SA トークン kubectl delete secret で対象を削除、ServiceAccount を再作成
暗号通貨ウォレット 秘密鍵を新しいウォレットに移行(既存ウォレットは廃棄)

重要: ローテーション作業は必ず感染していないクリーンな端末から実施してください。感染した環境で新しいクレデンシャルを生成しても、即座に窃取される恐れがあります。

ローテーションの優先順位は「npmトークン → GitHubトークン → その他」です。npmトークンを先に無効化することで、ワームのさらなる伝播を食い止められます。

STEP 2 — 依存関係のクリーンアップ

# node_modulesを完全削除
rm -rf node_modules

# npmキャッシュもクリア
npm cache clean --force

# 安全なバージョンにピン留めしてから再インストール
npm ci --ignore-scripts

Dockerを利用している場合は、node_modules をキャッシュしているレイヤーを含む全イメージを再ビルドしてください。

STEP 3 — 侵害の影響範囲調査

クレデンシャルを保護したうえで、実際の被害範囲を調査します。

GitHubの確認:

  • Organization の Audit log で不審なアクション(新規リポジトリ作成、ワークフロー変更、秘密情報へのアクセス)を検索
  • 自社が管理する npm パッケージの publish 履歴を確認(身に覚えのないバージョンがないか)

AWSの確認:

  • CloudTrail で感染時刻以降の不審なAPIコールを検索(特に IAM 操作、S3 アクセス、EC2 起動など)

npm の確認:

  • 自社公開パッケージのバージョン一覧を確認し、身に覚えのないリリースがないか精査

再発を防ぐ — 中長期の予防策

CI/CDで npm ci --ignore-scripts を標準化する

--ignore-scripts オプションは、ライフサイクルスクリプト(preinstall / postinstall 等)の実行を無効化します。これにより Stage 0 の侵入口を根本から遮断できます。

# GitHub Actions の例
- name: Install dependencies
  run: npm ci --ignore-scripts

一部のパッケージ(ネイティブアドオンなど)は postinstall でビルドが必要なため、--ignore-scripts を適用するとビルドが壊れることがあります。その場合は npm rebuild <パッケージ名> を後続ステップで個別実行し、必要なパッケージのみを許可リスト化してください。

.npmrc に検疫期間を設定する

# .npmrc
min-release-age=7

このオプションを設定すると、公開から7日未満のパッケージバージョンはインストールされません。今回の攻撃は「公開直後の数十分〜数時間」が最も危険な時間帯でした。7日間の検疫期間を設けることで、セキュリティ機関が汚染パッケージを検出・報告するまでの時間を稼ぎ、被害を回避できる可能性が高まります。

多層的な検証レイヤーを組む

Provenanceだけに頼らず、複数のレイヤーで防御します。

  • 依存関係スキャナの導入: Snyk / Socket / Aikido などのツールをCIパイプラインに組み込む
  • SBOM(ソフトウェア部品表)の生成: npm sbom や CycloneDX でサプライチェーンを可視化
  • 社内レジストリプロキシの導入: Verdaccio や Artifactory を経由させ、承認済みパッケージのみを許可するフロー

依存関係を「減らす」という根本対策

実際の開発現場では、意識せずに transitive dependency(間接依存関係)が膨らみがちです。定期的な棚卸しを実施し、小さなユーティリティパッケージは自前実装に置き換える判断も検討してください。

判断基準の目安:

  • そのパッケージが提供する機能を、10〜20行のコードで代替できるなら置き換えを検討
  • 週次ダウンロード数が少ない、メンテナーが1人のみ、といったパッケージは特に注意

これは孤立した事件ではない — 2026年npm攻撃の連鎖

攻撃のタイムライン

今回の事件は突然降って湧いたものではなく、2026年を通じた一連の攻撃の延長線上にあります。

時期 事案
2026年3月 axiosパッケージへの侵害
2026年4〜5月 Mini Shai-Hulud(第1波)
2026年7月 AsyncAPI 侵害
2026年8月4日 keyv / cacheable 侵害(第2波「Shai-Hulud Returns」)

攻撃はなぜ高度化・連続化しているのか

根本的な理由は、「メンテナアカウント」という攻撃対象の非対称性にあります。たった1人のアカウントを乗っ取ることで、そのメンテナーが管理する数百のパッケージに一挙にアクセスできます。攻撃者にとってコストパフォーマンスが極めて高い手口です。

加えて、今回のような自己伝播型ワームは「攻撃開始後のコスト」が逓減します。最初の侵害さえ成功すれば、あとは自動的に感染が広がるため、攻撃者は手を動かす必要がありません。

次に何が来るか — エンジニアが持つべき前提

ベストプラクティスとして、これからの開発者は次の前提を持つ必要があります。

「信頼できるパッケージ」は存在しない。「検証し続ける仕組み」だけが残る。

Zero Trust アーキテクチャの考え方をサプライチェーンにも適用する時代になりました。Provenance証明、依存関係スキャン、SBOM、最小権限の原則——これらを組み合わせた多層防御が、今後のスタンダードになるでしょう。


まとめ — 今日から使えるチェックリスト

この記事の内容を、行動に落とし込んだチェックリストです。チーム全員で確認することをお勧めします。

  • npm ls keyv 2>/dev/null | grep '6.0.0' で該当バージョンを確認した
  • ロックファイル(package-lock.json / yarn.lock / pnpm-lock.yaml)に keyv 6.0.0 が含まれていないか確認した
  • CI/CDのビルドログ(2026年8月4日 18:35 JST以降)を確認した
  • 感染が疑われる場合、クリーンな端末から全クレデンシャルをローテーションした
  • GitHub Audit log と AWS CloudTrail で不審な挙動を確認した
  • 自社が管理するnpmパッケージに不審なバージョンが公開されていないか確認した
  • CI/CDに npm ci --ignore-scripts を導入した
  • .npmrcmin-release-age=7 を設定した
  • 依存関係スキャナ(Snyk / Socket / Aikido など)をCIパイプラインに組み込んだ
  • チーム内に本件を共有した

参考リンク


サプライチェーン攻撃は、私たちが日常的に信頼しているエコシステムそのものを標的にします。怖い話で終わらせず、今日この記事を読んだその日に、まずチェックリストの上から3項目だけでも実行してみてください。段階的に対策を強化していくことが、長期的なセキュリティ向上につながります。

コメント

0/2000