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

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

node.js npm security oss

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

脆弱性の概要

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スクリプトの冒頭に埋め込まれたら?

ES2016の追加機能が決定、あるいはES7言うな問題

ecmascipt

今年6月に公開予定の ECMAScript 2016 (ES7) で追加される機能が以下の2つに決定した。

今後は3月1日にスペックのスナップショットが切られ、6月の公開に向けた作業が始まる

あれ、ES7ってそれだけなの?と思った方は、ぜひ ECMAScript の新しい策定プロセスを確認されたし。

標準化には2つの実装が要求される

昨年公開されたES6以降、ECMAScript の仕様策定プロセスは機能ベースで毎年リリースするスタイルになった。

細かいプロセスは 『ECMAScriptの仕様策定に関するカンニングペーパー | Web Scratch』 を参照してもらうとして、重要なポイントは、提案機能が標準化される(Stage 4になる)ためには2つ以上の実装が必要ということ。

今回のES2016については、1月26-28日にSans Franciscoで開かれたTC39 Meetingをもって実装の締め切り宣言がなされた。

Exponentiation Operator の実装状況は、EdgeがPreview版にフラグ付きで実装済みSpiderMonkey (Firefox Nightly) では実装済み、という状況。これで実装が2つとカウントされたことから、少なくとも4大ブラウザに実装されればフラグ付きだったりStable版でなかったりしてもカウントされると考えて良さそう。V8には実装されたけどChromeには載ってない、とかだとどうなんだろう。

ちなみに Async/Await は Edge Preview にフラグ付きで実装されただけだったので、実装2つには足りず ES2017 に持ち越された。

ES7 言うな問題

ES2016 (ES7) が確定したので、改めて。

Firefox Dev Confのスライド にも書いたのだけど、ECMAScriptの新しい仕様策定プロセスの不理解や過去の経緯から "ES7 Decorator" や "ES7 Async/Await" という言葉が使われがちという問題があった。いよいよES7の機能セットが確定して明らかな誤用と言えるレベルになってきたので、さすがにもう使うべきでない。

Stage 3以下の提案中の仕様については、ES.nextとかES proposalsと言及するか、あるいは単にESと言う方がまだ誤解が少ない。

domenicも言っているようにECMAScript においてはもはやバージョン番号は重要ではないのだから。

追記

ときを同じくして Dr. Axel 先生も同じようなエントリを書いてた。

If you are disappointed that your favorite stage 3 feature did not make it into ES2016 – don’t worry: With the new release process, it’s more about the stage a proposal is in than what release it is a part of. As soon as a proposal reaches stage 4, it is done and safe to use.

The final feature set of ECMAScript 2016 (ES7)

書かれてる通り、ある機能が ES2016 に入るか入らないかはあまり重要ではない。その機能が Stage 4 に上がる(実装が2つ揃う)のがいつなのかが重要なのであって、その時点が含まれるのが ES2016 か ES2017 なのかは年度の切り替えがいつかという手続き上の問題に過ぎないので。

追追記

Jay Phelpsがほぼ同じ記事をポストしている。

彼はES.nextよりもさらに誤解の少なくなるよう、"stage-1 decorators" のように "stage-x" を付けて呼ぶか、または単に "proposed features" と呼ぶことを推奨している。

npm initでauthorやlicenseなどの初期値を指定する

npm node.js advent calendar

JavaScript Advent Calendar 2015の2日目。小ネタです。

npm initするときにauthorとかlicenseとか毎回同じこと入力するの面倒だったりしません?実はいくつかの項目は初期値を設定できるのだけど、ググってもあまり日本語情報が無いようなので共有。

設定方法は.npmrcで以下のように指定すればOK。

init-author-name=Teppei Sato
init-author-email=teppeis@gmail.com

.npmrcの場所は以下の順で優先される。(詳細は公式を参照)

  1. per-project config file (/path/to/my/project/.npmrc)
  2. per-user config file (~/.npmrc)
  3. global config file ($PREFIX/etc/npmrc)
  4. npm builtin config file (/path/to/npm/npmrc)

今回は全プロジェクトに指定したいケースなので、ホームディレクトリの~/.npmrcに書いておくのが良いと思う。

指定できる設定値は公式docを参照。init-で始まる項目をリストアップしてみる。

init-author-name, init-author-email

名前とメールアドレス。これはたぶんみんな便利になるはず。

init-author-url

自分はURL書いてないので使ってない。

init-license

デフォルトのライセンスはISCなので、自分はMITを初期値に指定してる。

init-version

デフォルトが1.0.0の強気設定なので、0.0.1から始めたい弱気な人はどうぞ。

init-module

これを指定するとnpm initが呼ばれたときにロードするJSファイルを置き換えることができる。よりカスタマイズしたい上級者向け。

完全に置き換わるので、いつもの対話的にパッケージ名等を聞かれる処理はいっさいなくなっちゃうから必要なら自分でゼロから書かなければいけない。実はinit-moduleの初期値は~/.npm-init.jsになっていて、ファイルがあれば発動するようになっているので、init-module自体を設定しなくてもファイルを置けば利用可能。

npm initの実体は npm/init-package-json パッケージで、そのうち default-input.js がデフォルトの対話式スクリプトの実体なので、これをベースに改造するのが良いかも。


以上です。

あとnpm initとは関係ないnpmrcの小ネタとして、tag-version-prefix を変更するとnpm versionで"v1.0.1"になるのを"1.0.1"にできるようになるよ。

Enjoy happy npm life!

『よもだそば(日本橋)』カレー Advent Calendar 2015 1日目

curry advent calendar

カレー Advent Calendar 2015(あと3人募集中!)の1日目です。

2013年は家の近所のカレー屋を紹介したのですが、今年は会社の近くの行きつけである『よもだそば』を紹介します。

続きを読む

npm v2のインストールは`npm i -g npm@latest-2`

npm node.js

タイトルで言い切った感のある小ネタ。

最新のnpm v3は基本素晴らしいのだけど、主にnode_modulesがフラットになったなどのbreaking changesによって一部のパッケージがまだ非対応だったり、自分のプロジェクトで固めたshrinkwrap.jsonからは対応前の古いパッケージを参照していたりすることが稀によくある。

そういうケースではnpm v2系を使い続けたいのだけど、インストール時に

$ npm i -g npm@2

でインストールするとv2系の正式リリース版ではなく、プレリリース版が入ってしまう。

npmプロジェクトではリリースをdist-tagsで管理していて、まずRC版をnextタグとしてプレリリースをして、問題なければ次のリリースでlatestとして正式リリースするプロセスになっている。 v3系がリリースされたので上記のタグはv3系が使ってしまっているが、v2系にもちゃんとタグが付いている。こんな感じ。

  'dist-tags': 
   { latest: '3.5.0',
     next: '3.5.1',
     'latest-2': '2.14.12',
     'v3.x-latest': '3.5.0',
     '3.x-latest': '3.5.0',
     '3.x-next': '3.5.1',
     'v3.x-next': '3.5.1',
     'next-2': '2.14.13',
     'latest-1': '1.4.29' },

npm@2だとnext-2相当がインストールされちゃうので、無駄なリスクを減らしたいプロジェクトではlatest-2をインストールしておくとたぶん無難。

$ npm i -g npm@latest-2

とはいえ、いまさらv2系に大きな変更は入らないと思うのでどっちでもあんまり影響はないと思うけど。

関係ないけど、v3系のdist-tagsはちょっと命名規則が違うんですね。v3.x3.xの両方用意されてたり謎にやさしいし。

Spring Boot 1.2/1.3でhot swapping的なこと

java spring-boot

唐突にJavaの話。Spring Bootで変更を動的に反映する場合(いわゆるhot swappingとかhot deploy)はSpring Loadedを使えって出てくるのだけど、なんか動いたり動かなかったりしてなんでなんだろうと小一時間調べたメモ。Spring Boot + Maven + IntelliJ IDEA 15 on Mac 前提。

続きを読む

明日の次世代Webカンファレンスでjs_nextやります

イベントの趣旨はconnpassを見てもらうとして、自分は11:10から js_next というセッションでオーナーをやります。 メンツは@t_wadaさん、@constellationさん、@vvakameさんという盤石の布陣。

このセッションでは、JavaScriptの未来はどうなっていくのか、ブラウザで動くプログラミング言語はどうなっていくのか、というテーマを扱います。

キーワードはこのあたり?

全然このとおりにならない可能性高いですけど。 Reactだとかそっち系は別の front_arch というのセッションで扱うので、こちらではやりません。

500人も集まらないだろうから受付係いらないね、とか言ってたのにフタを開けたら1100人集まっちゃって青ざめてますが、ご興味ある方はぜひどうぞ。

まあ、一番の問題は会場設営で登壇者8:30集合なんだけど、そこに何人集まるかってことなんですけどね。