JavaScriptフロントエンドエキスパートチームの小林(@koba04)です。先日、npmから脆弱性についての発表がありました。調べていく中でいくつか思うところがあったので解説も兼ねて書いていきたいと思います。CLInpmの利用者としてやるべきことは、npmのバージョンを6.13.4以上にあげるyarnのバージョンを1.21.1以上にあげるです。(yarnのバージョンは別途あげる必要があります)。nodejs.orgnpmによる発表では、今回発表された脆弱性は2件あるため、それぞれ個別に考えます。binに任意のパスを指定出来る件npmパッケージはpackage.jsonのbinフィールドに、インストールすることで使えるようになるコマンドを指定出来ます。eslintの場合は下記のように指定されているため、インストールするとeslintコマンドがnpm-scriptsやnpx経由で利用可能になります。github.comパッケージをグローバルにインストールしない場合、このコマンドはnode_modules/.binに配置されます。npm-scriptsの実行時はここにPATHが通っているため、npm-scriptsではeslintコマンドとして実行できます。今回は脆弱性の話なので詳しく解説しませんが、binに興味持った人は下記のドキュメントを参照してください。docs.npmjs.com今回の脆弱性はここのコマンド名の部分に、../../cdなどと指定したら...?という話です。現状では特にコマンド名に対するチェックが行われていなかったため、インストールするだけで任意のファイルを置き換えることが可能であり、これが脆弱性であると判定されました。npmでは、この辺りのcommitで修正されています(内部で利用している別パッケージの修正)github.comyarnでは、この辺りのcommitで修正されていますgithub.comこの脆弱性は、孫依存のような開発者自身が明示的に指定せずにインストールされる依存パッケージからでも悪用可能なため、対応すべきです。グローバルにインストールしたパッケージが別のコマンドを上書きできる件npmはプロジェクトローカルにパッケージをインストールするだけでなく--global(-g)フラグを使うことでグローバルにパッケージをインストールできます。yarnの場合は、globalコマンドを利用します。グローバルにパッケージをインストールした場合、パッケージが提供するコマンドのシンボリックリンクが/usr/local/bin以下に作成されます(作成される場所は実行環境により異なります)。/usr/local/binといえば、他にもHomebrewなどでインストールしたパッケージのコマンドも配置される場所です。この脆弱性は、グローバルにインストールされたパッケージが、binに指定しているコマンドによってすでにある別のコマンドを上書きできるというものです。"evil.js"のように書かれていると、jqコマンドが上書きされてしまうということです。グローバルにインストールすること自体がそんなに多くないのと、よくわからないnpmパッケージをグローバルにインストールすることはほとんどないと思うので、1つめのものに比べると深刻度は低いかなと思います。npmでは、この辺りのcommitで修正しています。github.com対応方法としては、すでにコマンドが存在する場合、シンボリックリンク先が今からインストールしようとしているものと同じかどうか確認しています。yarnでは、これに対する修正はまだ行われていません。下記のissueでどうするのか聞いてみました。github.com上記のIssueによると、yarnでは今回の件を下記の3つに分けて考えています。plantingは1つ目に紹介した脆弱性で、前述した通りyarnは1.21.1で修正済みです。ように指定することで任意のポイントに対するシンボリックリンクを作成できるというものです。plantingの対応と同時に対応されています。こちらに関しては、脆弱性ではなくbugfixという扱いにしています。overlapは2つ目に紹介した件であり、yarnでは対応されていないものです。対応しない理由としては、上記のIssueでは下記のように説明されています。Changeとなるためこれらのパッケージは、ユーザーが意図してインストールしたパッケージであるためそのため、特にグローバルなパッケージをインストールする場合には、注意深く行う必要があります。上記のIssueによると、yarnはv2でbinスクリプトはNodeを通じてのみ実行可能にすることを計画しているようです。これにより、Node上でサンドボックス環境を構築してその上で実行させることが可能にできる可能性があります。yarnはNodeがこのようなセキュリティポリシーを実装することを期待しています。postinstallとinstall今回の脆弱性についてはここまでですが、前述したIssueではpostinstallの危険性についても言及されています。npmでは、publishやpackなど、任意のタイミングで呼ばれるscriptをnpm-scriptsとして登録出来る仕組みがあります。どのようなscriptを登録出来るのかは下記のページより確認可能です。docs.npmjs.comその中に、installとpostinstallがあります。これらは利用者がパッケージをインストールする際に実行されるスクリプトです。installするだけでインストールしたパッケージに対して任意のスクリプト実行を許可していることになります。installするだけで悪意のあるスクリプトを実行されてしまいます。今回のような脆弱性を利用せずとも悪意のあるパッケージはうまく依存関係に含まれることに成功すれば任意のスクリプトをユーザー環境で実行できます。そのための対策として、npm-scriptsの実行をしないための--ignore-scriptsというオプションがあります。docs.npmjs.comこれを利用することでインストール時に任意のスクリプトの実行を拒否することが可能です。これを使うためにはなぜinstallやpostinstallが使われているのかを理解する必要があります。現状、下記の2点の用途で利用されていることが多いと思います。C++などのコードを含んだネイティブモジュールのビルド環境に応じたバイナリのダウンロードFundingのお願い1のケースで言えば、fseventsやpuppeteerなどがよく目にケースではないでしょうか。fseventsに関しては下記のPRでinstallスクリプトを実行しないようになっているので、最新版だとインストールスクリプトは実行されません。github.compuppeteerでは下記のスクリプトでchromiumのバイナリをダウンロードします。これは、多くの場合postinstallを使ってconsole.logでメッセージを出力することで実現されています。fundコマンドで出力することができます。fundに対応したパッケージが依存にあると、下記のような形で出力されます。fundを実行すると下記のような形で出力されます。fundコマンドが利用可能になります。fundを使うための準備が整ったとも言えます。yarnは現在fundコマンドをサポートしていませんが、前述したIssueによるとv2には実装してバックポートすることが検討されているようです。C++で書いている場合には、WebAssemblyで置き換えることが可能かもしれません。NodeがサポートしていないネイティブのAPIを使いたい場合には、現時点ではまだ使い続ける必要がありそうです。将来的にはこれについてもWebAssemblyで解決出来る可能性はあるかもしれませんが。packageを作ってみたので興味ある人は自身のプロジェクトで実行してみてください。install-scriptsgithub.comこちらは社内のプロジェクトで実行した結果です。この中で動作に必要なscriptはfseventsとpuppeteerだけでした。他は全てfunding関連です。node_modules/styled-components/package.jsonfetch-mockは最新版ではすでにfundを使うようになっていますcore-jsについてはこちらで議論されていますが、今のところ削除する予定はなさそうですgithub.comstyled-componentsについては過去に議論されたIssueがなかったので確認してみましたgithub.com下記のように組み合わせることで必要なスクリプトのみを実行することも可能です。install-scriptsyarnはpostinstallについて、既存のエコシステムを壊してしまうのでv2で全て無効にすることは難しいですが、v3では考えているようです。現時点でも手動でホワイトリスト管理によって許可されたscriptのみ実行可能にすることは可能だとしています。実際にpostinstall、installを調べてみた結果としては、そこまで多くないなという感想だったので--ignore-scriptsを使った運用するのも可能であるように感じました。packageを作ってみようかなと思いました。Requestを送りました。github.com以前には下記のevent-streamに対する問題がありましたが、現状ではインストールするパッケージを全て把握することは現実的ではありません。incident今回の脆弱性を通じて、npmの現状や将来の方向性などを考える機会になればいいなと思ってこの記事を書きました。08:00関連記事2019-08-20Puppeteerで不要なCSSを消すこんにちは。Node用プラグインを書いた話こんにちは、アプリケーション基盤チームの渡辺です。パッケージング入門こんにちは。作り直した話こんにちは。Systemチームの金子です。LINKサイボウズ・ラボサイボウズ株式会社プライバシーポリシースターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。