LinkedInの求人メッセージから送られたGitHubリポジトリを、そのままnpm installしたら端末が乗っ取られる——そんな手口が、開発者Roman Imankulov氏の実例として明らかになりました。人の勘とAIエージェントによるコード精査が被害を防いだ一方、GitHubはnpm 12でインストール時のスクリプト実行を既定で止める方針です。

この記事では、攻撃の仕組みと防御のポイント、npm 12で変わる既定動作を整理します。

  • 偽LinkedIn求人から仕込まれるnpm install攻撃の手口
  • バックドアが隠れていたファイルと起動の流れ
  • AIエージェントが人間の見落としを補った経緯
  • npm 12(2026年7月予定)で変わる3つの既定設定
  • 今からできる対策コマンド

偽求人が仕向ける「コードレビュー」の罠

フルスタックのPython開発者であるImankulov氏は、小規模な暗号資産スタートアップのリクルーターからLinkedInで連絡を受けました。数日のやり取りの末、動かないPoC(概念実証)のリードエンジニア募集として、公開GitHubリポジトリのレビューを依頼されます。具体的には「非推奨のNodeモジュールの問題を確認してほしい」とのことでした(参考)。

コードベースの事前確認は珍しくありません。ただ氏は違和感を覚え、過去に報じられていたソーシャルエンジニアリング攻撃を思い出して警戒しました(参考)。

氏はローカル環境ではなく、Hetzner上に使い捨てのVPSを立ち上げ、そこにリポジトリをクローンしました。続けてPiというコーディングエージェント(内部でCodexを利用)を読み取り専用モードで走らせ、ファイル読み取り系のツールだけを有効にしてコード全体を精査させています。

エージェントはすぐにapp/test/index.jsで処理を止め、「このコードを実行するな、離れろ」と警告を返しました。氏自身がざっと読んだときは「下手なコードだが害はなさそう」と感じていた部分で、エージェントだけが脆弱性を特定したと語っています(参考)。

npm installだけで動くバックドア

攻撃の核心は、テストファイルに見せかけた250行規模のapp/test/index.jsです。定数をつなぎ合わせてhttps://rest-icon-handler.store/icons/77というURLを組み立て、サーバーから返ってきた内容をそのまま実行する仕組みが、コメントアウトされたテストコードの間に埋め込まれていました(参考)。

起動経路は次のとおりです。

  1. app/index.jsrequire('./test')でテストファイルを読み込む
  2. package.jsonprepareスクリプトがnode app/index.jsを実行する
  3. npm installの際、prepareは自動で走る

つまりテストコマンドを打たなくても、依存関係を入れるだけでバックドアが動きます。「非推奨モジュールの確認」という依頼は、被害者にnpm installを実行させるための餌でした(参考)。

独立系のオープンソース・セキュリティアーキテクトであるDevashri Datta氏は、The Registerへの寄稿で次のように指摘しています。攻撃者は不審なバイナリの実行ではなく、開発者が無意識に繰り返すnpm installに依存している。package.jsonのライフサイクルフック(ライフサイクルフックとは、パッケージのインストール前後などに自動実行されるスクリプトのこと)に実行ロジックを埋め込む手法は新しくないが、開発者がオートパイロットでインストールを走らせる現場では依然として有効だ、と述べています(参考)。

借りた顔で信頼を作る手口

リポジトリの39件のコミットは、実在するフルスタックエンジニアの名前とメールで記録されていました。氏が別の口実で連絡を取ると、本人は「その会社で働いたことはなく、GitHubでなりすましを何度も受けている」と答えました(参考)。

リクルーターのプロフィールも、実在する文化系ジャーナリストのものを流用したとみられます。氏が「インストールできない」と伝えると、非技術的な経歴のはずの相手が突然npmやNodeのバージョン議論を始め、npm installの実行を促してきたとImankulov氏は記しています(参考)。

The Registerは、北朝鮮系とみられる詐欺グループが偽面接や偽求人で開発者アカウントを狙う事例も相次いでいると報じています。LinkedInは偽アカウントを大量に削除していると公表していますが、2025年1〜6月だけでもユーザー通報後に制限されたアカウントは38万6000件にのぼり、2024年後半の26万6000件、2021年同期の8万6000件から増加傾向にあります(参考)。

AIエージェントが担う防御的コードレビュー

Datta氏は、Imankulov氏の対応が「信頼できないコードはサンドボックスで隔離するか手動レビューする」という従来の指針に、ローカルAIエージェントを組み合わせた新しい衛生管理の例だと評価しています。疲労や社会的プレッシャーの影響を受けないエージェントは、テストスイートが外部へ接続して未検証コードを取得しようとするような異常動作を数秒で表面化できる、と述べています(参考)。

氏は「攻撃の話は聞いていたが、自分が標的になるとは思わなかった。疲れた日ならnpm installを先に打っていた」と振り返っています。個人の安全のため、スクリプトを既定で実行しないpnpmへ切り替えたとも語っています(参考)。

企業のサプライチェーンセキュリティ(ソフトウェア部品の供給経路を守る取り組み)の観点では、攻撃は社内ネットワークに入る前の開発者端末を狙う「左シフト」が進んでいる、という指摘も出ています。面接の名目で端末が侵害されると、SSH鍵やクラウドトークン、社内リポジトリへの生きたアクセスが漏れる恐れがあります(参考)。

npm 12が変える既定の3点

GitHubが保守するnpmは、2026年7月頃のメジャーバージョン12でnpm installの既定動作をセキュリティ寄りに切り替えます。変更点はすでにnpm 11.16.0以降で警告として確認できます(参考)。

設定 v12の既定 効果
allowScripts off 依存パッケージのpreinstallinstallpostinstallを実行しない。binding.gypを持つネイティブモジュールの暗黙のnode-gyp rebuildも対象
--allow-git none Git URL由来の依存を解決しない。.npmrc経由でGit実行ファイルを差し替える経路を塞ぐ
--allow-remote none HTTPSターボールなどリモートURL由来の依存を解決しない

GitHubのプロダクトマネージャーLeo Balter氏は、コミュニティ投稿で「インストール時のライフサイクルスクリプトはnpmエコシステム最大のコード実行面。依存ツリーのどこか1つが侵害されれば開発端末やCIで任意コードが走る。既定で止め、信頼するパッケージだけ明示的に許可する」と説明しています(参考)。

なおv12でブロック対象に含まれるのは主に依存パッケージ側のスクリプトです。今回のようにレビュー対象リポジトリ自身のpackage.jsonに仕込まれたprepareは、引き続きpackage.jsonを読んでから判断する必要があります。未知のリポジトリではサンドボックス実行とnpm install --ignore-scriptsの併用が有効です。

今からできる準備

公式ドキュメントが推奨する手順は次のとおりです(参考)。

  1. npm 11.16.0以降へ上げ、通常のインストール時に出る警告を確認する
  2. npm approve-scripts --allow-scripts-pendingでスクリプトを持つパッケージを洗い出す
  3. 信頼するものだけnpm approve-scriptsで許可し、不要なものはnpm deny-scriptsで拒否する
  4. 生成された許可リストをpackage.jsonに書き込み、リポジトリへコミットする

v12移行後は、許可リストに載っていない依存のインストールスクリプトは動きません。CIでも同じpackage.jsonが参照されるため、許可リストのコミット漏れはビルド失敗の直接原因になります。

LinkedIn経由で送られたリポジトリを触る場面では、次も実践的です。

  • ローカルではなく隔離されたVMやクラウドの使い捨て環境で作業する
  • インストール前にpackage.jsonscripts欄を確認する
  • 読み取り専用ツールだけ有効にしたAIエージェントで異常なネットワーク接続や文字列分割を探す

Imankulov氏の事例は、サプライチェーン攻撃が「有名パッケージの侵害」だけにとどまらず、個人への偽求人という入口にも広がっていることを示しています。npm 12は依存ツリー経由の自動実行を抑えますが、人間のワークフローに乗った攻撃への警戒は、エコシステムの更新だけでは代替できません。