世界を動かす技術を、日本語で。

「これを防ぐ方法はない」と語る、これが定期的に発生する唯一のパッケージマネージャー

概要

  • npmレジストリで発生した大規模なサプライチェーン攻撃による影響
  • JavaScript開発者コミュニティの反応と危機感
  • 他エコシステム(GoやRust等)との対比
  • npmの構造的な脆弱性と既存対策の限界
  • 今後のリスクとコミュニティの姿勢

npmサプライチェーン攻撃の衝撃

  • npmレジストリ におけるサプライチェーン攻撃発生、数百万のエンタープライズアプリケーションへの被害
  • 数十億件 のユーザーデータ流出という甚大な影響
  • JavaScript開発者コミュニティの 深い悲しみ と「不可避だった」との諦念
  • 40階層以上 の未検証パッケージ依存構造の危険性
    • 一つの文字列操作のために多数のパッケージを利用する現状
  • 放置されたユーティリティパッケージが 乗っ取られ 暗号通貨マイナー等の悪意あるコード注入
  • 「これは 自然災害 のようなもの」との声

Node.jsエコシステムの現実

  • Node.jsエコシステム全体で 予測不可能な悲劇 として受け止められる現状
  • DevOpsチームが AWSキーのローテーション 等、緊急対応に追われる状況
  • Thoughts and prayers (お悔やみ)を送る開発者たち

他エコシステムとの比較

  • GoやRust、ネイティブWeb API利用者は被害ゼロを報告
    • 標準ライブラリの充実により サードパーティ依存の最小化
    • 暗号学的検証 がツールチェーンに組み込まれている堅牢性
    • 学生の趣味プロジェクトが グローバルインフラを破壊 する事態の未発生

npmの構造的課題とコミュニティの姿勢

  • npmスポークスパーソン、「 防ぎようがない」との立場
  • レジストリが 任意のインストールスクリプトをデフォルトで実行 する設計
  • 次の侵害 まで心を強く持つしかない」という消極的な対応姿勢
  • レジストリポリシービルドサンドボックス による抜本的対策の難しさ
  • 悪意ある行為者の存在を 受け入れるしかない 現状認識

今後の展望と課題

  • サプライチェーン攻撃に対する 根本的な構造改革 の必要性
  • 標準ライブラリ強化や 依存パッケージの厳格な審査 体制の検討余地
  • コミュニティ全体 での意識改革とセキュリティ文化の醸成

Hackerたちの意見

背景を知らない人のために: https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...

同じ雰囲気: https://www.youtube.com/watch?v=lOTyUfOHgas

オニオンの記事はまだ残ってるから、リンクできるよ。

goやRustがPython/npmに対して実際にどんな保証をしてるの? Python/npmが狙われやすいだけなのかな? 最近はサードパーティのパッケージを避けるようにしてるんだ。

goのgo:generateワークフローも、npm経由で広がるワームを作るのに悪用される可能性があると思う。例えば、ハードドライブ全体をスクレイピングしてgitプロジェクトを探し出し、go.modの依存関係をパッチするプログラムを作れるし、これをツールチェーンスクリプトとしてgoで書くこともできる。NPMの弱点は、ユーザーが介入できない任意のコマンドやシェルスクリプトを実行できるpre/postinstallステップだね。依存関係は隔離されたchrootサンドボックス、もしくはコンテナ内で実行するべきだ。それがこの問題を軽減する唯一の方法だと思う。オペレーティングシステムのファイルシステムと開発ワークフローのファイルシステムを分離する必要があるからね。それに、ほとんどのホストベースのファイアウォールはバイナリごとに設定されていて、コマンドラインごとではない。だから「python」や「nodejs」がネットワークアクセスを許可される一方で、「nodejs myworm.js」みたいな具体的なコマンドには対応できない。だから、ファイアウォールはこの手のマルウェアに対してはあまり役に立たないよ。

Python/npmが狙われやすいだけなのかな? 攻撃者は被害者がいるところに行くんだ。フロントエンドはほとんどがNPMを使ってるモノカルチャーだし、バックエンドはそうでもない。これはNPMの言い訳にはならないけど、また一つの弱点だね。フロントエンドとバックエンドの開発者の違いについて深いポイントを指摘することもできるけど、そこには触れないよ。

最後に確認したとき、npmには公開用の2FAがあったけど、cargoにはなかった。cargoはnpmより良いとは思わないけど、単に魅力的なターゲットじゃないだけだね。

正直なところ、Rustも同じ供給チェーン攻撃のパターンを持ってるよ。今は新しくてメンテナンスもされてるけど、10年後にはどうなるかわからないね。

一般的に、他のパッケージマネージャーもあまり良くないよね。特に、crates.ioやcargoはNPMと同じような問題を抱えていて、その言い訳の文言も妙に似てる。プログラミング言語やその周辺エコシステムのデザインやアーキテクチャについて面白いのは、「コアチーム」に与えられる大きな影響力だよね。コア言語の開発者が1人いると、1,000人の人気パッケージ開発者がいて、その下には1,000,000人のソフトウェア開発者がいて、さらに1,000,000,000人のユーザーがいる。つまり、ピラミッドの頂点で手を抜くと、下の層でその影響が大きくなるってこと。log4jみたいな「トップ1000」のパッケージにセキュリティの脆弱性があれば、数十億ドルの経済的損害や、何世代にもわたる修正作業が必要になることもある。でも、奇妙なことに、上の2つのレベルの資金はほとんどゼロに近い!ほとんどのプロジェクトは慈善事業で、道端で小銭を乞うような状態。最も使われているライブラリの中には、ボランティアの努力によるものも多いのに、グローバルなeコマースを支えているんだよね! cough-OpenSSL-cough。結果的に、問題を解決する力を持っている人たちが、解決するための資金が最も少ないってこと。だから、NPMやCrates.ioは、GoogleやMicrosoftなどの主要な出版社のアイデンティティを確認したり、名前空間を追加するような基本的なセキュリティチェックすら拒否しているんだ。これは一定の労力がかかるし、技術的には簡単で、今は監視も安くできるけど、彼らの小さな予算を圧迫する可能性が高い。例外として、NuGetのようにしっかりした資金提供があるパッケージマネージャーがあって、Microsoftからの安定した資金で内部(営利!)のワークフローを支えているのと同じくらい、外部の「無料」ユーザーもサポートしている。「無料でオープン」って素晴らしいけど、結局は払った分だけの価値があるってことだね。[1] ほとんどの人がすぐに思い浮かべられる名前だよね:Guido van Rossum、Larry Wall、Kerningham & Richieとか。

パッケージや名前空間の所有権をどう管理するかは、パッケージマネージャーの管理者次第だよ。Maven Centralは何十年も存在していて、名前空間を盗まれる事件はほとんどない。例えば、「com.ycombinator」というgroupIdでパッケージを公開するには、ycombinator.comのドメインを所有していることを確認する方法が必要なんだ。そして、一度パッケージが公開されると、それは100%不変で、たとえ悪意のあるコードが含まれていても変更できない。確かに、そのライブラリは脆弱性があるとしてどこでも警告されるけど、NPMがMaven Centralのようなガードレールを長い間再現できなかったのは本当に不思議だよ。

なし。ターゲットとなる人口が少ないだけ。

記事が指摘しているポイントの一部は、他の人気のある言語は包括的な標準ライブラリを持っているってことだよね。JSは驚くほど小さいライブラリしかない。言語に付属する検証済みのライブラリセットがないから、アプリケーションは自分で作るか、サードパーティのパッケージリポジトリから引っ張ってくる必要がある。人々にNIH(自分でやらなきゃ)を叩き込んできたから、パッケージを手に取る傾向があるんだ。それ自体は悪いことじゃないけど、必要以上のコードを引っ張ってくることが多い。JSエコシステムは小さなモジュールを好むから、たくさんのモジュールが必要になるし、みんながその上に構築することで依存関係のグラフが膨れ上がる。意図的であれそうでないにせよ、問題が起こる可能性が高いんだ。ほかの言語では、最初から多くの機能が揃っている。確かにバグやセキュリティの問題はあったけど、JSエコシステムで見るものに比べればほんの少しだよ。他の言語では、外部の依存関係グラフがずっと小さくて、コア機能は信頼できるサードパーティから提供されている。

ポストインストールスクリプトが存在する正当な理由はないと思う。npmチームは大人になって、「npmバージョン○○からは、${today}より前に公開されたパッケージのポストインストールスクリプトだけを実行します」と宣言すべきだよ。

でも、これじゃ問題は解決しないよね。パッケージコードはビルド時やテスト中にも実行されるから。ちょっと範囲を制限するだけかもしれないけど。

インストールスクリプトは気を散らすだけで、パッケージの署名も同じように気を散らす要素だよ。どちらかの機能を追加したり削除したりしても、このパッケージエコシステムの感染力には大きな影響はない。インストールされたnpmコードは、ほぼ例外なしに実行されるから。

...そして、--dangerously-run-postinstall-scriptsオプションを使わない限り、ポストインストールスクリプトが見つかるとエラーが報告されるよ。これが必要なネイティブコードにリンクしたり、シムをコンパイルしたりするパッケージには影響が出るけど、そういうパッケージはごく少数だね。

セキュリティの問題は置いといて、企業環境ではインターネットやOSへのアクセスが厳しく制限されているから、ほんとに厄介だよね。

Rustのパッケージが自分でビルドするときにサンドボックスなしで動くってのも、あんまり正当性がないよね。

敬意を表して言うけど、ポストインストールスクリプトは完全にミスリードだよ。君がそれに驚いてるのは、他の誰かが制御してるコードが君の環境で動くからで、悪いことをする可能性があるからだよね。そう、確かにそうだし、そうなる可能性もある。でも、パッケージ内の通常のコードも同じなんだよ!インストール時には動かないけど、その中の何かは動くはずだよ。そうじゃなきゃ、依存関係に含まれることはないからね。ポストインストールスクリプトを排除すれば、悪用率に長期的な影響があると思うのは、問題を深く考えてない証拠だよ。残念ながら、この問題はTFAが示唆するよりもずっと複雑なんだ。単に「スイッチの隣に翼が落ちるボタンを置くのをやめよう」って話じゃなくて、私たちが防ぎたいこと(他人の悪いコードが自分の環境で動くこと)と、私たちが欲しいこと(他人の良いコードが自分の環境で動くこと)を区別するのは、ものすごく手間がかかる作業が必要なんだよ。手間を避けるのが、そもそも他人のコードを動かそうと考える理由なんだからさ。

最近、人気のパッケージのいくつかのポストインストールスクリプトを監査したんだけど、主にネイティブバイナリを使ったり、それをダウンロードしたり、プラットフォームが互換性があるかを検出したり、ノードによってブートストラップされるのではなく直接リンクしたり、古いバージョンのnpmの問題を回避したりする内容が多かったよ。最近のノード/npmと一般的なOS/プラットフォームを使っているなら、正当な理由なしにすべてのポストインストールスクリプトを無効にできるはずだよ。

いろんな職場で、全開発者のマシンに安全なグローバルnpm設定をインストールするのがめちゃくちゃ大変だった。みんなにそれを無効にしないでって頼んだり、mdmツールでチェックしたり。もっと安全な初期設定が早く出てほしいわ。

npmは使わない方がいいよ。デフォルトでpostinstallを実行しないパッケージマネージャーを使おう。切り替えはめっちゃ簡単だから。

これはパッケージの問題じゃなくて、メンタルヘルスの危機だよ。

被害を受けた人たちに思いを馳せます。

またこんなスレッドを hijack しちゃってごめんね。クールダウンについて意見があるのはわかってるけど、クールダウンがあればaxiosやtanstack、他の最近のnpmサプライチェーン攻撃から守れるよ。大企業で働いてるなら、すでにArtifactoryやNexusでクールダウンが設定されてるかもしれないし、もしなければ簡単に設定できるよ。なんでクールダウン?ほとんどのnpm(やpypi)の妥協は数時間以内に発覚してるから、クールダウンを設定することで、N日(1日でも3日でも、7日はちょっとやりすぎかもだけど)より新しいリリース日を持つパッケージを無視することができるんだ。どうやって設定するかって?最新のpnpmを使えば、デフォルトで1日のクールダウンが追加されるよ。https://pnpm.io/supply-chain-security それか、ワンクリックで解決したいなら、https://depsguard.com を使ってみて。npm、pnpm、yarn、bun、uv、dependabot、renovabotにクールダウンや他の推奨設定を追加するCLIだよ。あと、https://cooldowns.dev もあるけど、こっちはクールダウンに特化してて、ローカルでの設定を手伝うスクリプトもあるよ。全部オープンソースで無料だから、~/.npmrcを編集できるなら、特に必要ないかもしれないけど、ワンクリックで解決したい人には役立つかも。注意点として、新しい重要なCVEをパッチする必要がある場合は、クールダウンをバイパスする必要があるけど、どれもその方法があるよ。ここ数週間、具体的な数字はないけど、ソフトウェアサプライチェーン攻撃(悪意のあるバージョンが押し込まれること)からのリスクが、新しいゼロデイCVEからのリスクよりも多いように見える。注意:depsguardを管理してるよ。

そうだね、pnpmがv11でデフォルトで1日のクールダウンを追加したのは素晴らしいことだよ。

自分はC++とConanを使って、自分のレシピや事前ビルドしたアーティファクトでやってる。これでかなりリスクを軽減できてるよ。依存関係がインターネットに依存して、何百万ものユーザーが各パッケージに手を加えるって考えたやつ、ほんとに企業環境にとっていいアイデアだと思ったのかな… こんな風に危険にさらされることがどれだけあるか、考えただけでクレイジーだよ。

これは文化的な問題だね。すでにうまく動いているものに対して、最新のパッケージにアップデートしたくなる衝動が常にあるんだ。しかも、適用可能かどうかを確認するために変更履歴を読むことすらしない。クールダウンは、メンテナーに少しの忍耐を強いる手段に過ぎないけど、効果はあるんだよ。

答えはLLM検査だね。残念ながら、これはソフトウェアのコストを上げることになる。特に悪意のあるLLMがバックドアをより上手く隠し始めるとね。長期的には、私の意見ではCHERIが答えになるべきだよ。