読者です 読者をやめる 読者になる 読者になる

npm install scriptの脆弱性とオープンソースと信頼

先日アナウンスされた脆弱性とその周辺について、とりとめなく。

脆弱性の概要

VU#319816 によれば、今回問題になっているのはnpmの以下の性質を利用するとnpmパッケージでワーム(自己増殖力のあるマルウェア)を作れるというもの。

  • 依存パッケージのバージョンをロックせず、semverにより範囲指定することが多い
  • CLIで一度npmへloginすると、明示的にnpm logoutするまで認証が永続化される
  • npm registry が中央集権型サーバーである

具体的な手法として、Chris Contoliniが PoC として pizza-party というリポジトリを公開している*1。以下のように動作する。

  1. ワームが仕込まれたパッケージを依存関係に含むパッケージを npm install する
  2. ワームの実体であるinstallスクリプトが実行される
  3. ワームは親のnode_modulesにある全てのパッケージに対して、
    1. package.jsonのinstallスクリプトにワームを仕込む(このような
    2. npm version patchでパッチバージョン番号を上げる
    3. npm publishでワーム入りパッケージとしてアップデートを公開

Chris Contoliniのブログによれば、99.99%はそのパッケージのオーナーではないのでnpm publishが失敗するだろうが、npmの外部パッケージ依存度の高さから一部のオーナーに感染すれば雪だるま式に拡散するだろう、と主張している。

IMO: ローカルのnode_modulesを探索するより、npm whoamiからユーザー名を取得して npmjs.com から所有パッケージを根こそぎダウンロードしてワーム仕込んだ方が効率的な気がする。

何が問題か?

installスクリプトに攻撃を仕込まれる危険性については、まあある意味当然というか仕様だし、1年前に rimrafall という、installスクリプトrm -rf /*するPoCパッケージが公開された騒動でも話題になった。これに対してnpm inc.は問題あるパッケージを都度削除する対応を取った。

今回のポイントは、ワームが仕込まれてても同じことできるの?ということ。有名パッケージも含めて大量のパッケージがガシガシ感染していったらどうする?都度削除とかムリだし取り返しつかない状態になるのでは?と。

たしかに、substackやsindresorhusが感染したらと想像するとやばそう。

npmブログの見解としては、publishの頻度をモニタリングしてるから、ワームが広がり始めたら全体のpublishを止めるよ、と言っている。

どうしたらいいの?

さて各ユーザーはどう対応したら良いのか。

--ignore-scripts

npmはブログでは以下の方法が紹介している。

  • npm install--ignore-scriptsオプションを付けてスクリプトを実行させない
  • npm config set ignore-scripts trueで上記動作をデフォルトに設定する

しかし、npm自身もブログで書いていることであるけど、installスクリプトを防いだところで、そのパッケージ自身に悪意のコードがあればrequireした時点で攻撃されてしまう。感染力を弱める効果は多少あるかもしれないけど、攻撃コードの実行を防ぐという意味では正直気休め程度かなと思う*2

installスクリプトが動かないことでインストールできないパッケージもあったりして面倒(行儀悪いけど)。

npm shrinkwrap

shrinkwrapを使って子孫すべての依存関係をロックして、感染したパッケージがアップデートしたバージョンの混入を防ぐ方法。

依存関係のアップデートするときどうするの?というのはあるけど、感染力はだいぶ弱まりそう。 ただ、ライブラリ系だと現実的には運用が厳しいし、全パッケージがshrinkwrapし始めたらnpmの思想を根本的には壊しかねない気がする。

npm logout

こまめにnpm logoutして、npm publishなど必要なときだけログインする方法。

ログアウトしておけば、自分自身の環境での悪意のコード実行は防げないけど、自分が所有するパッケージへの感染による拡散は防げる。認証が必要な操作って普通はpublishぐらいなので、普通の人は頻繁に使わないから問題なさそう。

npmさん、ぜひログイン状態を永続化せず都度パスワードを聞くようにするオプションを付けて欲しい。

npmブログによれば、2-factor authを準備中らしい。

オープンソースと信頼

ということで、今回のワームの拡散は認証周りをちゃんとやれば防げそうそうだけど、根本的な問題である悪意のあるコードの実行を防ぐのは難しい。

npmブログから引用すると、

You should not execute any software downloaded from the Internet if you do not trust it, including software downloaded from npm.

The npm Blog — Package install scripts vulnerability

「インターネットからダウンロードした信頼できないソフトウェアを実行すべきではない、npmも含めて

当たり前ではあるのだけど、当のnpmに改めて言われるとなかなか重い。

また、Chris Contoliniのブログにはこう書いてある。

Using other people’s code is inherently risky

Building an npm worm - Chris Contolini

普段は目をそらしがちではあるのだけど、オープンソースだからといってセキュアなわけではないし、誰かが見てくれている保証もない。

こういう問題ってモダン≒オープンソースをベースにしたソフトウェア開発では共通する問題だと思うけど、多くの細かい外部パッケージに依存しがちなNode.js世界では特に大きな影響がある。

ちょうど先日のkik/left-pad問題にもつながる話だ。

マイクロパッケージ文化には光の側面闇の側面がある。

自分が依存するパッケージ、その子孫までの中にどこにも悪意のコードが無いことを自信を持って言えるだろうか?ねずみ算式に増えるので自分ですべてチェックするのは現実的にはムリだろう。

ただの考えすぎで善意が解決してくれる問題なのか、プロジェクトの性質によって対応を考えればよいのか。他の言語では何か仕組みがあるのかな?できればnpmの自由な雰囲気と両立できる何かがほしい。

よーしわかった、セキュアな大統一汎用標準ライブラリがあればいいんだ!あ、そうだ、Closure Library っていうちょうど良いものがあるじゃないですか!なんだ、すべては杞憂だったのか(違う)。

*1:npmには公開されてない。取扱注意

*2:例えばmainスクリプトの冒頭に埋め込まれたら?