npm install scriptの脆弱性とオープンソースと信頼
先日アナウンスされた脆弱性とその周辺について、とりとめなく。
脆弱性の概要
VU#319816 によれば、今回問題になっているのはnpmの以下の性質を利用するとnpmパッケージでワーム(自己増殖力のあるマルウェア)を作れるというもの。
- 依存パッケージのバージョンをロックせず、semverにより範囲指定することが多い
- CLIで一度npmへloginすると、明示的に
npm logout
するまで認証が永続化される - npm registry が中央集権型サーバーである
具体的な手法として、Chris Contoliniが PoC として pizza-party というリポジトリを公開している*1。以下のように動作する。
- ワームが仕込まれたパッケージを依存関係に含むパッケージを
npm install
する - ワームの実体であるinstallスクリプトが実行される
- ワームは親の
node_modules
にある全てのパッケージに対して、
Chris Contoliniのブログによれば、99.99%はそのパッケージのオーナーではないのでnpm publish
が失敗するだろうが、npmの外部パッケージ依存度の高さから一部のオーナーに感染すれば雪だるま式に拡散するだろう、と主張している。
IMO: ローカルのnode_modules
を探索するより、npm whoami
からユーザー名を取得して npmjs.com から所有パッケージを根こそぎダウンロードしてワーム仕込んだ方が効率的な気がする。
何が問題か?
installスクリプトに攻撃を仕込まれる危険性については、まあある意味当然というか仕様だし、1年前に rimrafall という、installスクリプトでrm -rf /*
するPoCパッケージが公開された騒動でも話題になった。これに対してnpm inc.は問題あるパッケージを都度削除する対応を取った。
`rimrafall` has been taken off the @npmjs registry. Thanks to all who let us know about it.
— Forrest L Norvell (@othiym23) 2015年1月26日
今回のポイントは、ワームが仕込まれてても同じことできるの?ということ。有名パッケージも含めて大量のパッケージがガシガシ感染していったらどうする?都度削除とかムリだし取り返しつかない状態になるのでは?と。
たしかに、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.
「インターネットからダウンロードした信頼できないソフトウェアを実行すべきではない、npmも含めて」
当たり前ではあるのだけど、当のnpmに改めて言われるとなかなか重い。
また、Chris Contoliniのブログにはこう書いてある。
Using other people’s code is inherently risky
普段は目をそらしがちではあるのだけど、オープンソースだからといってセキュアなわけではないし、誰かが見てくれている保証もない。
こういう問題ってモダン≒オープンソースをベースにしたソフトウェア開発では共通する問題だと思うけど、多くの細かい外部パッケージに依存しがちなNode.js世界では特に大きな影響がある。
ちょうど先日のkik/left-pad問題にもつながる話だ。
自分が依存するパッケージ、その子孫までの中にどこにも悪意のコードが無いことを自信を持って言えるだろうか?ねずみ算式に増えるので自分ですべてチェックするのは現実的にはムリだろう。
ただの考えすぎで善意が解決してくれる問題なのか、プロジェクトの性質によって対応を考えればよいのか。他の言語では何か仕組みがあるのかな?できればnpmの自由な雰囲気と両立できる何かがほしい。
よーしわかった、セキュアな大統一汎用標準ライブラリがあればいいんだ!あ、そうだ、Closure Library っていうちょうど良いものがあるじゃないですか!なんだ、すべては杞憂だったのか(違う)。
3分で分かるAngularJSセキュリティ
先日のng-mtg#4 AngularJS 勉強会でLTしようと思ったけど申し込みが間に合わなかったのでブログに書きます。
先月リリースされたAngularJS 1.2はセキュリティがんばってる的なことを聞いたので、セキュリティ周りの仕組みを調べてみました。
お題は以下です。
- CSRF
- JSON
- CSP (Content Security Policy)
- Escaping