• #npm
  • #サプライチェーン攻撃
  • #セキュリティ
  • #pnpm
  • #npmrc
開発eurekapu-nuxt4メモ

npmサプライチェーン攻撃 Shai-Hulud 2.0 への防御設定

claude-code-syncのログを眺めていたら「Shai-Hulud 2.0」という名前が目に飛び込んできた。npm史上最悪のサプライチェーン攻撃だという。正規パッケージのメンテナー認証情報を盗み取り、そのまま悪意あるバージョンをnpmに公開する手口で、npm audit では検出できない。正規パッケージが汚染されるので、依存関係のハッシュだけ見ていても気づけない。

手が止まった。自分のプロジェクトは大丈夫なのか、すぐに確認を始めた。


影響チェック: 感染の兆候なし

複数のチェックを並行で走らせた。

  • pnpm audit で既知の脆弱性をスキャン
  • pnpm-lock.yaml の最近の変更差分を確認(意図しないバージョン変更がないか)
  • 依存パッケージの公開日時を確認(直近24時間以内に公開されたバージョンがないか)

結果、感染の兆候は見つからなかった。ひとまず安心したが、攻撃の手口を考えると「今回たまたま踏まなかっただけ」にすぎない。次の波が来る前に防御を張ることにした。


防御設定: minimum-release-age

pnpmには minimum-release-age という設定がある。npmレジストリに公開されてから指定時間が経過していないバージョンのインストールをブロックする。公開直後に仕込まれた悪意あるバージョンは、コミュニティが異変に気づいて取り下げられるまでに数時間〜数日かかる。その「空白の時間」にインストールしないようにするのが狙いだ。

3日(4320分)を設定した。

# ~/.npmrc
minimum-release-age=4320

設定の置き場所を変えた

最初は pnpm-workspace.yaml に書いた。

# pnpm-workspace.yaml(最初の設定 → 削除済み)
packages:
  - "apps/*"
onlyBuiltDependencies:
  - "@libsql/linux-x64-gnu"
  - ...
minimumReleaseAge: 4320

動作は確認できたが、これだとこのモノレポでしか効かない。ほかのプロジェクトで pnpm install したときは素通しになる。

全プロジェクトで効かせるため、~/.npmrc に移動した。グローバル設定なので、どのディレクトリで pnpm install を叩いても3日ルールが適用される。プロジェクトレベルの pnpm-workspace.yaml からは minimumReleaseAge の行を削除して重複を消した。


まとめ

  • Shai-Hulud 2.0 は正規パッケージのメンテナー認証情報を盗んで悪意あるバージョンを公開する攻撃。npm audit では検出できない
  • プロジェクトの影響チェックを実施し、感染の兆候なしを確認
  • minimum-release-age=4320(3日)を ~/.npmrc にグローバル設定
  • 公開から3日以内のバージョンは全プロジェクトでブロックされる
  • 完全な防御ではないが、「公開直後の汚染バージョンを踏む」リスクを減らせる