npmのセキュリティに関するベストプラクティス10選
Juan Picado
2019年2月19日
0 分で読めますnpmの脆弱性が気になりますか?フロントエンドとバックエンドの開発者は、npmのセキュリティに関するベストプラクティスを考慮することが重要です。オープンソースのセキュリティ監査は、セキュリティを開発の早い段階に組み込むうえで欠かせません。また、公式のnpmコマンドラインツールでさえ脆弱性が見つかったことから、npmパッケージのセキュリティを最優先事項にする必要があります。
今回のチートシートでは、オープンソースのメンテナーと開発者の双方に役立つ、npmのセキュリティに関するベストプラクティスと生産性向上のヒントを10個ご紹介します。それでは、npmのセキュリティに関するベストプラクティス10選を見ていきましょう。まずは、公開するnpmパッケージにパスワードを追加してしまうという、よくあるミスから始めます。
1. npmレジストリにシークレットを公開しない
APIキーやパスワードなどのシークレットは、ソース管理に誤って含まれたり、公開npmレジストリに公開したパッケージに紛れ込んだりする可能性があります。作業ディレクトリ内の.envなどの指定ファイルにシークレットが含まれている場合は、SCMへのコミットを防ぐため.gitignoreに追加する必要があります。しかし、プロジェクトのディレクトリからnpmパッケージを公開するとどうなるでしょうか?
npm CLIは、レジストリに送信するため、プロジェクトをtarアーカイブ(tarball)にまとめます。tarballに追加されるファイルとディレクトリは、次の条件で決まります。
.gitignoreまたは.npmignoreファイルがある場合、その内容がパッケージ公開時の除外パターンとして使用されます。両方の除外ファイルがある場合、
.npmignoreに記載されていないものはすべてレジストリに公開されます。この仕様は混乱を招きやすく、シークレットの漏えいにつながる可能性があります。開発者が.gitignoreを更新しても.npmignoreの更新を忘れると、機密情報を含む可能性のあるファイルがソース管理には送られなくても、npmパッケージには含まれてしまうことがあります。
もう1つの有効な方法は、package.jsonのfilesプロパティを使うことです。これはホワイトリストとして機能し、作成・インストールするパッケージに含めるファイルの配列を指定します(除外ファイルはブラックリストとして機能します)。filesプロパティと除外ファイルは併用でき、パッケージに含めるファイルと除外するファイルを明示できます。両方を使用する場合、前者であるpackage.jsonのfilesプロパティが除外ファイルより優先されます。
パッケージの公開時、npm CLIは作成されるアーカイブの内容を詳細に表示します。さらに慎重を期すには、公開コマンドに--dry-run引数を追加しましょう。実際にレジストリへ公開することなく、tarballがどのように作成されるかを事前に確認できます。
2019年1月、npmはブログで、パッケージとともにトークンが公開されたことを検知すると、トークンを自動的に無効化する仕組みを追加したと発表しました。
2. ロックファイルを必ず使用する
パッケージロックファイルの登場は大歓迎でした。環境を問わずインストール結果が決定的になり、チームでの共同作業でも依存関係を確実に管理できるようになったからです。これで安心!……と思っていました。しかし、package.jsonを変更したのに、ロックファイルを一緒にコミットし忘れたらどうなるでしょうか?
依存関係のインストール時、Yarnもnpmも同じように動作します。プロジェクトのpackage.jsonとロックファイルに不整合があると、package.jsonのマニフェストに基づいて変更を補い、ロックファイルに記録されているものとは異なるバージョンをインストールします。
このような状況は、意図しないパッケージのバージョンが取り込まれ、ロックファイルの利点が失われる可能性があるため、ビルド環境や本番環境では危険です。
幸い、ロックファイルに記載された依存関係とバージョンに従うよう、Yarnとnpmの両方に指示する方法があります。不整合があればインストールは中断されます。コマンドラインでは次のように実行します。
Yarnを使っている場合は、
yarn install --frozen-lockfileを実行します。npmを使っている場合は、
npm ciを実行します。
3. run-scriptsを無効にして攻撃対象領域を減らす
npm CLIはパッケージのrun-scriptsを利用します。npm startやnpm testを実行したことがあれば、run-scriptsを使ったことになります。npm CLIは、パッケージが宣言できるスクリプトを利用します。パッケージは、プロジェクトへのインストール中に特定のタイミングで実行するスクリプトを定義できます。たとえば、スクリプトフックの中には、インストールされるパッケージが後処理を行うために実行するpostinstallスクリプトがあります。
この機能を悪用すると、攻撃者はパッケージを作成・改変し、インストール時に任意のコマンドを実行させることができます。実際に起きた事例として、npmトークンを窃取した人気パッケージeslint-scopeのインシデントや、crossenvのインシデントがあります。さらに、npmレジストリでタイポスクワッティング攻撃を悪用したほかの36個のパッケージも確認されています。
悪意のあるモジュールによる攻撃対象領域を減らすため、次のnpmセキュリティのベストプラクティスを実践しましょう。
インストールするサードパーティ製モジュールは、健全性と信頼性を確認するため、必ず十分な調査と審査を行いましょう。
新しいバージョンへむやみにアップグレードするのは避けましょう。試す前に、新しいパッケージのバージョンがある程度普及するのを待ちます。
アップグレードする前に、対象バージョンの変更履歴とリリースノートを必ず確認しましょう。
パッケージをインストールする際は、
--ignore-scriptsを追加して、サードパーティ製パッケージのスクリプト実行を無効にしましょう。プロジェクトの
.npmrcファイル、またはnpmのグローバル設定にignore-scriptsを追加することも検討してください。
4. npmプロジェクトの健全性を評価する
依存関係の更新状況
リリースノートやコードの変更を確認せず、新しいバージョンを十分にテストしないまま、依存関係を常に最新に更新するのは、必ずしも良い方法とはいえません。一方で、依存関係を長期間更新しなかったり、まったく更新しなかったりすることも、問題の原因になります。
npm CLIでは、セマンティックバージョニング上の指定との差に基づいて、依存関係が最新かどうかを確認できます。npm outdatedを実行すると、古くなっているパッケージを確認できます。

「黄色で表示された依存関係は、package.jsonのマニフェストで指定されたセマンティックバージョニングに対応しています。赤色で表示された依存関係には更新があります。また、出力には各依存関係の最新バージョンも表示されます。」
診断ツールを実行する
複数のNode.jsパッケージマネージャーがあり、パスには異なるバージョンのNode.jsがインストールされていることもあります。npmのインストールと作業環境が正常かどうか、どう確認すればよいでしょうか?開発環境でもCI環境でも、npm CLIが期待どおりに動作することを確かめるのは重要です。
診断ツールを実行しましょう!npm CLIには、npmとの連携が正常に機能しているか環境を診断するツールがあります。npm doctorを実行して、npmの設定を確認しましょう。
公式npmレジストリに接続できることを確認し、現在設定されているレジストリを表示します。
Gitが利用可能か確認します。
インストールされているnpmとNode.jsのバージョンを確認します。
ローカルおよびグローバルの
node_modulesや、パッケージキャッシュ用フォルダーなど、各種フォルダーのアクセス権を確認します。ローカルnpmモジュールキャッシュのチェックサムが正しいか確認します。
5. オープンソースの依存関係にある脆弱性を監査する
npmエコシステムは、あらゆるプログラミング言語のエコシステムの中でも、アプリケーションライブラリが最も多く集まるリポジトリです。レジストリとそこに登録されたライブラリは、JavaScript開発者にとって中核となる存在です。他の開発者が作成した成果を活用し、自分のコードベースに取り込めるためです。一方、アプリケーションでオープンソースライブラリの利用が広がるにつれ、セキュリティ脆弱性が持ち込まれるリスクも高まっています。
人気の高いnpmパッケージの多くで脆弱性が見つかっており、プロジェクトの依存関係を適切にセキュリティ監査しなければ、大きなリスクを招くおそれがあります。たとえば、npm request、superagent、mongooseのほか、jsonwebtokenやnpm validatorといったセキュリティ関連パッケージにも脆弱性が見つかっています。
パッケージのインストール時に脆弱性をスキャンするだけでは、セキュリティ対策は完了しません。ソフトウェア開発ライフサイクル全体を通して効果的に取り入れられるよう、開発者のワークフローに組み込み、コードのデプロイ後も継続的に監視する必要があります。
脆弱性をスキャンする
Snykを使って脆弱性をスキャンし、npmのセキュリティに関するベストプラクティスを実践しましょう。次のように実行します。
Snykテストを実行すると、検出された脆弱性と脆弱な依存関係の経路が表示されるため、依存関係ツリーをたどって、どのモジュールが脆弱性を持ち込んだかを把握できます。さらに重要なのは、Snykが実行可能な修正方法を提示することです。Snykがリポジトリに自動でプルリクエストを作成し、修正済みバージョンへのアップグレードを支援します。修正がない場合は、脆弱性を緩和するためのパッチを適用できます。また、脆弱なパッケージに対して、semver上で可能な限り最小限のアップグレードを推奨するスマートアップグレードも提供します。
オープンソースライブラリで新たに見つかった脆弱性を監視する
セキュリティ対策はこれで終わりではありません。
アプリケーションのデプロイ後に、依存関係の脆弱性が見つかったらどうすればよいでしょうか?ここで重要になるのが、セキュリティ監視と、プロジェクトの開発ライフサイクルとの緊密な連携です。
GitHubやGitLabなどのソースコード管理(SCM)システムとSnykを連携することをおすすめします。Snykがプロジェクトを継続的に監視し、次の処理を行います。
脆弱な依存関係をアップグレードまたはパッチ適用するプルリクエストを自動で作成します
プルリクエストによってオープンソースライブラリに持ち込まれた脆弱性をスキャンして検出します
SCMとSnykを連携できない場合も、Snyk CLIツールからプロジェクトのスナップショットを送信して監視できます。次のコマンドを実行するだけです。
Snykとnpm auditの違いは?
Nearformが公開したブログ記事で、npmのauditとSnykの違いを比較しています。ぜひご覧ください。
Snykの脆弱性データベースは、脅威インテリジェンスシステムを通じて、脆弱性に関する包括的なデータを提供します。より広範な脆弱性をカバーし、CVEがまだ割り当てられていない脆弱性も検出して報告できます。たとえば、npmの勧告に含まれる脆弱性の72%は、まずSnyk Open Sourceの脆弱性データベースに追加されました。
6. ローカルnpmプロキシを使う
npmレジストリは、あらゆるJavaScript開発者が利用できる最大のパッケージコレクションであり、Web開発者向けオープンソースプロジェクトの大半が集まる場所でもあります。しかし、セキュリティ、デプロイ、パフォーマンスなどの要件によっては、別のレジストリが必要な場合もあります。そのようなとき、npmでは別のレジストリに切り替えられます。
npm installを実行すると、すべての依存関係を解決するため、メインのレジストリとの通信が自動的に開始されます。別のレジストリを使う場合も、簡単に設定できます。
npm set registryを設定して、デフォルトのレジストリを指定します。単一のコマンドでレジストリを指定するには、
--registry引数を使います。
Verdaccioは、設定不要で使えるシンプルかつ軽量なプライベートレジストリです。次のように簡単にインストールできます。

自分専用のレジストリを、これほど簡単に運用できるようになりました。このツールの主な機能を見てみましょう。
npmレジストリ形式に対応しており、プライベートパッケージ機能、スコープ対応、パッケージのアクセス制御、Webインターフェースでのユーザー認証を利用できます。
リモートレジストリとの接続や、依存関係ごとに異なるレジストリへ振り分け、tarballをキャッシュする機能を備えています。重複ダウンロードを減らし、ローカル開発環境やCIサーバーの帯域幅を節約するために、すべての依存関係をプロキシ経由にしましょう。
デフォルトの認証プロバイダーとしてhtpasswdによる認証を使用しますが、GitLab、Bitbucket、LDAPにも対応しています。独自の認証プロバイダーを使用することもできます。
ストレージプロバイダーを変更することで、簡単にスケールできます。
プロジェクトがDockerベースの場合は、公式イメージを使うのが最適です。
テスト環境を非常にすばやく立ち上げられるほか、大規模なモノレポプロジェクトのテストにも便利です。
実行方法はとても簡単です。
ローカルのプライベートレジストリにverdaccioを使う場合は、パッケージをローカルレジストリに公開するよう設定し、開発者が誤ってパブリックレジストリに公開するのを防ぎましょう。そのために、package.jsonに次の設定を追加します。
レジストリが起動しました!さあ、パッケージを公開するには、npmコマンド npm publish を使うだけです。これで、世界中の人と共有できます。
7. セキュリティ脆弱性を責任ある方法で開示する
セキュリティ脆弱性が見つかった場合、事前の警告やユーザーが自らを守るための適切な対策がないまま公開すると、深刻な脅威となるおそれがあります。
セキュリティ研究者には、責任ある開示プログラムに従うことが推奨されます。これは、脆弱な資産のベンダーやメンテナーと研究者をつなぎ、脆弱性、その影響、該当する範囲を伝えるためのプロセスとガイドラインです。脆弱性のトリアージが適切に行われたら、ベンダーと研究者が修正方法と脆弱性の公開日を調整します。セキュリティ上の問題を公表する前に、影響を受けるユーザーがアップグレードまたは修正できるようにするためです。
セキュリティは後回しにしたり、非倫理的に扱ったりしてよいものではありません。Snykはセキュリティコミュニティを大切にし、オープンソースパッケージの脆弱性を責任ある方法で開示することが、ユーザーの安全とプライバシーの確保につながると考えています。
Snykのセキュリティリサーチチームは、コミュニティと連携し、バグ報奨金プログラムに定期的に取り組んでいます。たとえば、f2e-serverの事例では、コミュニティから何百件もの脆弱性開示が寄せられました。また、バージニア工科大学などの学術研究者との緊密な連携を通じて、セキュリティの専門知識を提供し、ベンダーやコミュニティのメンテナーとの調整を支援しています。
ぜひ私たちと連携してください。脆弱性開示のプロセスについてもサポートします。
責任あるセキュリティ脆弱性の開示は、https://snyk.io/vulnerability-disclosureまたはメール(security@snyk.io)でご報告ください。
開示ポリシーはこちらをご覧ください。
8. 2FAを有効にする
2017年10月、npmは、npmレジストリで非公開およびオープンソースのパッケージをホストする開発者向けに、2要素認証(2FA)のサポートを正式に発表しました。
npmレジストリでは以前から2FAがサポートされていますが、普及はゆっくりと進んでいるようです。その一例が、2018年半ばのeslint-scopeインシデントです。ESLintチームの開発者アカウントが盗まれ、悪意ある第三者によって悪意のあるバージョンのeslint-scopeが公開されました。
** 緊急セキュリティ警告 ** 共有してください。
本日、eslint-scopeのバージョン3.7.2(https://t.co/Gkc9XhDRN6)に、NPMの認証情報を盗む悪意のあるコードが含まれていることが判明しました。バージョン3.7.2を使用している場合は、今すぐ対処してください。
Snyk DBのエントリー:https://t.co/dAhhA3cZQP
— Snyk (@snyksec) 2018年7月12日
2FAの有効化は、npmのセキュリティのベストプラクティスとして、手軽で大きな効果が期待できます。npmレジストリでは、ユーザーアカウントの2FAを次の2つのモードで有効にできます。
認証のみ:ユーザーがWebサイトまたはCLIからnpmにログインするときや、プロフィール情報の変更などの操作を行うときに認証します。
認証と書き込み:プロフィールやログインに関する操作に加え、トークンやパッケージの管理などの書き込み操作、およびチームやパッケージの公開範囲に関する一部の操作で認証します。
Google Authenticatorなどの認証アプリをモバイル端末にインストールすれば、準備完了です。アカウントの保護を2FAで強化するには、npmのユーザーインターフェースから有効にするのが簡単です。コマンドラインを使う場合も、対応するnpmクライアントのバージョン(>=5.5.1)であれば、簡単に2FAを有効にできます。
コマンドラインの手順に従って2FAを有効にし、緊急時用の認証コードを保存してください。ログインとプロフィール変更のみに2FAを有効にする場合は、上記のコード内の auth-and-writes を auth-only に置き換えてください。
9. npmのアクセストークンを使う
npm CLIにログインするたびに、ユーザー用のトークンが生成され、npmレジストリの認証に使われます。トークンを使うと、CIや自動化された処理でnpmレジストリ関連の操作を簡単に実行できます。たとえば、レジストリ上のプライベートモジュールへのアクセスや、ビルド工程からの新しいバージョンの公開などです。
トークンはnpmレジストリのWebサイト、またはnpmコマンドラインクライアントから管理できます。CLIを使って、特定のIPv4アドレス範囲に制限された読み取り専用トークンを作成する例を以下に示します。
自分のユーザー用に作成されたトークンの確認や、緊急時のトークンの無効化には、それぞれ npm token list または npm token revoke を使います。
npmトークンを保護し、露出を最小限に抑えて、このnpmセキュリティのベストプラクティスを実践しましょう。
10. モジュールの命名規則とタイポスクワッティング攻撃を理解する
パッケージを作成するとき、最初に行うことの一つがモジュールの命名です。最終的な名前を決める前に、npmが定めるパッケージ名のルールを確認しましょう。
214文字以内であること
ドットまたはアンダースコアで始まらないこと
名前に大文字を含まないこと
末尾にスペースを含まないこと
小文字のみを使うこと
一部の特殊文字は使用できません:「~\’!()*”)’」
.または_で始めることはできません
node_modulesまたはfavicon.icoは使用できません。禁止されています。
これらのルールに従っていても、新しいパッケージを公開する際にnpmがスパム検出を行う点に注意してください。スコアやパッケージ名が利用規約に違反していないかどうかに基づいて判定されます。条件に違反すると、レジストリによってリクエストが拒否される場合があります。
タイポスクワッティングとは、ユーザーの入力ミスなどを悪用する攻撃です。悪意ある第三者は、既存の人気モジュールに非常によく似た名前で、悪意のあるモジュールをnpmレジストリに公開する可能性があります。
私たちはnpmエコシステムで数十件の悪意あるパッケージを追跡してきました。PyPIのPythonレジストリでも同様の事例が確認されています。特に知られている事例として、cross-env、event-stream、eslint-scopeがあります。

タイポスクワッティング攻撃の主な標的の一つはユーザーの認証情報です。どのパッケージも、グローバル変数 process.env を通じて環境変数にアクセスできるためです。過去に確認された別の事例としてevent-streamがあります。この攻撃では、アプリケーションのソースコードに悪意のあるコードを注入しようと、開発者が標的にされました。
npmのセキュリティのベストプラクティス10選を締めくくる、こうした攻撃のリスクを減らすためのヒントをご紹介します。
パッケージのインストール手順をターミナルにコピー&ペーストするときは、特に注意してください。ソースコードリポジトリとnpmレジストリの両方で、インストールしようとしているパッケージに間違いがないか確認しましょう。
npm infoでパッケージのメタデータを取得すれば、コントリビューターや最新バージョンなどの詳細を確認できます。日々の作業では、npmからログアウトした状態をデフォルトにしましょう。認証情報が弱点となり、アカウントが簡単に侵害される事態を防げます。
パッケージをインストールするときは、任意のコマンドが実行されるリスクを減らすため、
--ignore-scriptsを追加しましょう。例:npm install my-malicious-package --ignore-scripts
チートシートを印刷して、目につく場所に貼っておきましょう。JavaScript開発者の方も、npmを楽しんで使っている方も、実践すべきnpmのセキュリティのベストプラクティスを思い出すのに役立ちます。
CTFを始めよう
オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。