攻撃を知り、防ぐ

サプライチェーン攻撃

依存パッケージに仕込まれる、インストール時に動くコードと、名前の似た偽パッケージを見つける方法を学びます。

  • レベル: 基礎
  • 約40分
  • 2026-11-03

#依存パッケージという入口

いまのアプリは、自分で書いたコードより、他人が書いたパッケージのほうが多くなりがちです。npm install を実行すると、自分で指定したパッケージだけでなく、そのパッケージが使う別のパッケージも、芋づる式に入ってきます。攻撃者は、その連なりのどこか 1 か所に入り込めば、それを使う人すべてに届きます。入り込み方は、おもに次の 3 つです。

手口起きること
乗っ取り正規のパッケージの公開権限を奪い、悪意ある版を公開する
偽物有名なパッケージに似せた名前(expresss など)で、偽のパッケージを公開する
仕込み正規に見えるパッケージに、インストール時に動くコードを入れる

実際にあった例です。2021 年 10 月、npm の人気パッケージ ua-parser-js の 3 つの版(0.7.29、0.8.0、1.0.0)で、悪意のあるコードが公開されました。修正版は 0.7.30、0.8.1、1.0.1 です(GitHub Advisory)。使う側の操作に誤りがあったわけではありません。いつもどおりに更新しただけで、入り込むおそれがありました。

なぜインストール時が狙われるのか

npm のパッケージは、preinstall、install、postinstall、prepare などの「ライフサイクルスクリプト」を持てます。これは、npm install を実行した人の権限で、インストールの途中に自動で実行されます。アプリを起動する前、コードを一行も読まないうちに、実行が終わります。

#インストール時に動くコードを一覧する

RoamSwitch の インストール時に動くコードの点検 は、node_modules の中にあるパッケージのライフサイクルスクリプトを、一覧にして見せます。何も実行しません。

スクリプトを持つパッケージを探す

無害なダミーを作ります。demo-helper はインストール時に動くスクリプトを持ち、plain-lib は持ちません。スクリプトが指す setup.js は存在しないので、実行されることもありません。

mkdir -p app/node_modules/demo-helper app/node_modules/plain-lib
echo '{ "name": "demo-helper", "version": "1.0.0", "scripts": { "postinstall": "node setup.js" } }' > app/node_modules/demo-helper/package.json
echo '{ "name": "plain-lib", "version": "2.0.0" }' > app/node_modules/plain-lib/package.json

# 確認済みの記録は ~/.config/roamswitch/ に保存されます。演習では、使い捨てのホームフォルダを使います
LAB_HOME=$(mktemp -d)
HOME=$LAB_HOME roamswitch scan-scripts ./app
出力の抜粋
これは脅威判定ではなく一覧表示です。何も実行しません。⚠️は誤検知を含む軽量ヒューリスティックです。

    demo-helper 1.0.0 [postinstall]
    node setup.js

確認済みの状態がまだありません。今のインストールスクリプトを確認したら、roamswitch scan-scripts --accept を実行してください。次回からは、増えた分だけを知らせます。

スクリプトを持つ demo-helper だけが出て、持たない plain-lib は出ません。判定ではなく一覧なので、悪意があるかどうかは、人が見て判断します。

「増えた分」だけを知る

一覧を確認したら、その状態を確認済みとして記録します。次の更新で、スクリプトを持つパッケージが増えたときだけ、知らせが出ます。

HOME=$LAB_HOME roamswitch scan-scripts --accept ./app

# 更新で、新しいパッケージが入ったと仮定します
mkdir -p app/node_modules/new-helper
echo '{ "name": "new-helper", "version": "1.0.0", "scripts": { "install": "node build.js" } }' > app/node_modules/new-helper/package.json
HOME=$LAB_HOME roamswitch scan-scripts ./app
出力の抜粋
確認済みの状態を記録しました。次回からは、増えた分だけを知らせます。

(…一覧…)

確認済みにした日(2026-11-03)の後で、インストール時に動くコードが増えたパッケージが 1 件あります。

● new-helper  - → 1.0.0  新しいパッケージがインストール時のコードを持っています (install)

数百のパッケージがあるのに、毎回一覧を読み直すのは現実的ではありません。「前回から増えたか」だけを見るようにすると、ua-parser-js のように、いつもどおりの更新に紛れた変化に気づきやすくなります。

#名前の似たパッケージを見つける

偽物は、有名なパッケージの名前に似せて作られます。打ち間違いを狙うので、typosquatting(綴り違いを狙う攻撃)と呼ばれます。名前の似たパッケージの判定 は、package.json に書かれた依存パッケージの名前を、有名な名前と比べます。

綴りの違いを調べる
cat > app/package.json <<'EOF'
{ "name": "demo-app", "version": "1.0.0",
  "dependencies": { "express": "^4.0.0", "expresss": "^4.0.0", "reakt": "^18.0.0", "lodahs": "^4.17.0" } }
EOF
roamswitch scan-typosquat ./app
出力の抜粋
これは静的な参考情報であり断定ではありません。ネットワーク接続は一切行いません。正規の類似パッケージが誤検知されることもあります。

⚠️  expresss は次に似ています: "express" (distance 1)  [./app]
⚠️  reakt は次に似ています: "react" (distance 1)  [./app]

expresss と reakt は見つかりました。express は正規の名前なので、出ません。一方、lodahs(lodash の綴りを入れ替えたもの)は出ていません。

ほかの綴り違いも、実機で試しました。

名前本物との違い見つかったか
expresss1 文字の追加(express)見つかった
reakt1 文字の置き換え(react)見つかった
recat隣り合う 2 文字の入れ替え(react)見つからない
axois隣り合う 2 文字の入れ替え(axios)見つからない
loadsh隣り合う 2 文字の入れ替え(lodash)見つからない
lodahs隣り合う 2 文字の入れ替え(lodash)見つからない

試した範囲では、1 文字の追加・置き換えは見つかり、隣り合う文字の入れ替えは見つかりませんでした。結果は、製品の版や、受け取った名前の一覧によって変わることがあります。「見つからなかった」ことは、安全の証明にはなりません。

#守りを組み立てる

RoamSwitch の一覧と検知は、対策の一部です。次のような対策を組み合わせます。

  • インストールを制御する: npm ci でロックファイルのとおりに入れます。スクリプトを実行したくないときは --ignore-scripts を付け、必要なパッケージにだけ、スクリプトを許します
  • 新しい版を、すぐには使わない: 公開された直後の版は使わず、数日待ちます。乗っ取られた版は、気づかれて取り下げられるまでの間が、いちばん危険です
  • スクリプトを閉じ込める: RoamSwitch の roamswitch install は、コマンドの説明によると、npm や pnpm の install を包んで、スクリプトを隔離した環境の中だけで動かします(この教材では、実行して確かめていません)
  • 増えた分を見る: 更新のたびに、「スクリプトを持つパッケージが増えたか」を確かめます
やってはいけないこと
  • 一覧に出たからといって、すべてを悪意のあるものと決めつける。ビルドに必要な正規のスクリプトも、たくさんあります
  • 一覧に出ないからといって、安全だと決めつける(下の「見えない部分」を読んでください)

#見えない部分

点検が見つけられないもの
  • スクリプトの中身は、読みません。一覧に出るのは、スクリプトがあるという事実と、そのコマンドだけです。悪意があるかどうかは、判定しません
  • スクリプトを使わない攻撃は、一覧に出ません。パッケージを読み込んだとき(require したとき)に動くコードは、ライフサイクルスクリプトではないので、対象外です
  • 名前の比較で見つかるのは、1 文字の追加と置き換えです(実機で確かめた範囲)。隣り合う文字の入れ替えは、見つかりませんでした
  • 比べる相手は、有名なパッケージの名前の一覧です。その一覧にない名前に似せた偽物は、比べようがありません
確認問題

更新後に scan-scripts を実行しても、増えたパッケージは何も表示されませんでした。確実に言えるのはどれですか

解説を見る

見ているのは、「インストール時に動くコードを持つパッケージが増えたか」だけです。読み込み時に動くコードや、すでにスクリプトを持つパッケージの中身が変わったことは、この点検では分かりません。

#次に学ぶこと

扱う機能

対応する攻撃手法 (ATT&CK): T1195.001

確認した内容

2026-11-03に確認しました。対象は Linux 1.17.30 です。Ubuntu 26.04 の Server Edition で、無害なダミーの node_modules に対して実行

コマンドの出力は、製品の版や環境によって変わります。違いが出たら、まず製品の版を確かめてください。