こんにちは!
IT用語には、Acronym 、頭文字の略称がたくさんありますよね。
便利ですが、時々 いい間違えや、会話がこんがらがることがあります。
Linuxコマンドのrpmと、
npmの字面が、rpmと似ていて、「全然、違うし!」と自分で、思わずツッコミをいれたくなったことが何度かありました。
npm → Node Package Manager
rpm → Red Hat Package Manager
今回はこちらの翻訳から、お届けします。
https://snyk.io/blog/ten-npm-security-best-practices/
#npmセキュリティ10のベストプラクティス
オープンソース アプリケーションのセキュリティ
Liran Tal, Juan Picado
リラン・タル、フアン・ピカド
2019年2月19日
npmの脆弱性が心配?
フロントエンドとバックエンドどちらのデベロッパにとっても、npmのセキュリティのベストプラクティスを考慮に入れることは重要です。オープンソースのセキュリティ監査は、セキュリティをシフトレフトするための重要な要素です。
公式のnpmコマンドラインツールにさえ脆弱性があることが判明しているように、npmパッケージのセキュリティは最重要課題であると言えるでしょう。
今回のチートシート編では、npmのセキュリティに関する10のベストプラクティスと、オープンソースのメンテナとデベロッパどちらにも役立つ生産性向上のヒントを中心にご紹介します。
それでは早速、10のnpmセキュリティベストプラクティスのリストから始めましょう。
まず、典型的なミスとして、公開するnpmパッケージにパスワードを追加する人がいます
#1.npmレジストリへのシークレットの公開を避ける
API キーやパスワードなどの機密情報は、ソースコントロールや公開されている npm レジストリのパッケージに簡単に漏れてしまいます。作業ディレクトリにある.env
のような指定のファイルにシークレットがあるかもしれません。SCM へのコミットを避けるために .gitignore
に追加する必要がありますが、プロジェクトのディレクトリから npm パッケージを公開するとどうなるでしょうか?
npm CLIは、プロジェクトをレジストリにプッシュするために、プロジェクトをtarアーカイブ(tarball)にまとめます。以下の基準で、どのファイルやディレクトリをtarballに追加するかを決定します。
-
.gitignore
または.gitignore
ファイルがある場合、パッケージの公開準備の際に、そのファイルの内容が無視パターンとして使用されます。 -
両方の無視ファイルが存在する場合、
.npmignore
にないものはすべてレジストリに公開されます。この状態は、よくある混乱の原因であり、シークレットの漏洩につながる問題です。デベロッパは.gitignore
ファイルを更新してしまうことがありますが、.npmignore
も更新するのを忘れてしまい、機密性の高いファイルがソースコントロールにプッシュされないまま、npm パッケージに含まれたままになってしまうことがあります。
オススメの方法としては、package.jsonのfiles
プロパティを利用することです。プロパティはホワイトリストとして機能し、作成・インストールされるパッケージに含めるファイルの配列を指定します(一方、ignoreファイルはブラックリストとして機能します) 。files
プロパティと ignore ファイルを併用することで、パッケージに含めるべきファイルと除外すべきファイルを明示的に決定することができます。両方を使用する場合、package.jsonの前者のfiles
プロパティがignoreファイルよりも優先されます。
パッケージが公開されると、npm CLIは作成されるアーカイブを冗長に表示します。念のため、publishコマンドに--dry-run
引数を追加して、実際にレジストリに公開せずにtarballがどのように作成されるかを最初に確認するようにしましょう。
2019年1月、npmは、トークンがパッケージと一緒に公開されていることを検知した場合、トークンを自動的に失効させる仕組みを追加したことをブログで公開しました。
#2.ロックファイルの実行
パッケージロックファイルの誕生により、異なる環境でも決定論的なインストールが可能になり、チームの共同作業でも依存関係の期待値が強化されました。便利ではなるのですが、もし仮に私がプロジェクトの package.json ファイルに変更を加え、ロックファイルをコミットするのを忘れていたら、どうなるでしょうか?
Yarnもnpmも、依存関係のインストール時には同じように動作します。プロジェクトのpackage.json
とロックファイルの間に不整合を検出すると、package.jsonの
マニフェストに基づいて、ロックファイルに記録されているものとは異なるバージョンをインストールすることで、その変更を補正します。
このような状況は、意図しないパッケージバージョンを取り込み、ロックファイルの利点がすべて無駄になってしまうため、ビルドや本番環境にとって危険です。
幸いなことに、ロックファイルから参照することで、Yarnとnpmの両方に、指定された依存関係のセットとそのバージョンを遵守するように伝える方法があります。不一致があるとインストールが中断されます。コマンドラインは以下のようになります。
- Yarnを使用している場合は、
yarn install --frozen-lockfile
を実行します。 - npm runを使用している場合は、
npm ci
を実行します。
#3.ランスクリプトを無視して攻撃対象を最小化
npm CLIはpackage run-scriptsで動作します。npm start
かnpm test
を実行したことがあれば、パッケージの実行スクリプトも使用したことがあるでしょう。npm CLIは、パッケージが宣言できるスクリプトに基づいて構築されており、パッケージがプロジェクトにインストールされている間、特定のエントリポイントで実行するスクリプトを定義することができます。例えば、これらのscript hook エントリのいくつかは、インストール中のパッケージがhousekeepingの雑用を実行するために実行するpostinstall
スクリプトであるかもしれません。。
この機能があると、悪質な攻撃者は、パッケージを作成・改変して、そのパッケージがインストールされたときに任意のコマンドを実行して悪質な行為を行うことができます。すでに起きている事例としては、npm トークンを盗み出した人気の eslint-scopeインシデントや、npm レジストリへのタイポスクワッティング攻撃を悪用した 36 のパッケージを含む crossenvインシデントなどがあります。
悪意のあるモジュールの攻撃対象を最小化するために、以下のnpmセキュリティのベストプラクティスを適用します。
インストールするサードパーティモジュールの健全性や信頼性を確認するために、常に吟味し、デューディリジェンスを行うことをお勧めします。
- やみくもに新しいバージョンにアップグレードするのは控えましょう。新しいパッケージのバージョンが出回るまで時間を置いてから試してみてください。
- アップグレードする前に、アップグレード後のバージョンの changelog や release notes を確認するようにしてください。
- パッケージをインストールする際には、必ず
--ignore-scripts
サフィックスを追加して、サードパーティ製パッケージによるスクリプトの実行を無効にしてください。 - ignore-scripts を .npmrc プロジェクトファイルやグローバルな npm 設定に追加することも検討してください。
#4.npmプロジェクトの健全性を評価
###古い依存関係
リリースノートやコードの変更点を確認したり、新しいアップグレードを総合的にテストしたりせずに、依存関係を常に最新のリリースに急いでアップグレードすることは、必ずしも良い習慣とは言えません。そうは言っても、古いままで全くアップグレードしなかったり、長い時間が経ってからアップグレードしたりするのも、トラブルの元になります。
npm CLIは、セマンティック・バージョニング・オフセットに関して、使用している依存関係の新鮮さについての情報を提供することができます。npm outdated
を実行すれば、どのパッケージが古くなっているかがわかります。
黄色で表示されている依存関係は、package.jsonマニフェストで指定されているセマンティック・バージョニングに対応しており、赤色で表示されている依存関係は、アップデートがあることを意味しています。さらに、出力には各依存関係の最新バージョンも表示されます。
###call doctor
様々なNode.jsパッケージマネージャや、パスにインストールされている様々なバージョンのNode.jsの間で、健全なnpmのインストールと作業環境をどのように確認しますか?開発環境でnpm CLIを使用している場合でも、CI内で使用している場合でも、すべてが期待通りに動作していることを評価することが重要です。
doctorを呼び出しましょう!
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のセキュリティのベストプラクティスに従うことができます。
$ npm install -g snyk
$ snyk test
Snykのテストを実行すると、Snykは発見した脆弱性をレポートし、脆弱性のあるパスを表示するので、依存関係のツリーを追跡して、どのモジュールが脆弱性をもたらしたかを理解することができます。最も重要なことは、Snykが実行可能な修正アドバイスを提供してくれることです。これにより、Snykがリポジトリに開いた自動プルリクエストを介して修正されたバージョンにアップグレードしたり、修正プログラムが利用できない場合にSnykが提供するパッチを適用して脆弱性を軽減したりすることができます。Snyk は、脆弱性のあるパッケージに対して可能な限り最小限の semver-upgrade を推奨することで、スマートなアップグレードを提供します。
####オープンソース・ライブラリに発見された脆弱性を監視する
セキュリティ対策はそれだけではありません。
アプリケーションがデプロイされた後に、アプリケーションの依存関係にセキュリティ脆弱性が発見された場合はどうでしょうか?ここで、セキュリティモニタリングとプロジェクトの開発ライフサイクルとの緊密な統合の重要性が出てきます。
SnykをGitHubやGitLabなどのソースコード管理(SCM)システムと統合し、Snykで積極的にプロジェクトを監視することをお勧めします。
- PRを自動的に開いて、脆弱な依存関係をアップグレードまたはパッチする
- プルリクエストによって導入された可能性のあるオープンソースライブラリの脆弱性をスキャンして検出する
SnykをSCMと統合できない場合は、Snyk CLIツールから送られてくるプロジェクトのスナップショットを監視することも可能です(実行するだけです)。
$ snyk monitor
####Snykはnpm監査とどう違うのですか?
- npmの監査ととSnykの違いを比較したNearformが公開したブログ記事をぜひご覧ください。
- Snykの脆弱性データベースは、脅威インテリジェンスシステムを通じて、脆弱性に関する包括的なデータを提供します。より良いカバレッジを提供し、まだCVEを受けていない脆弱性を表面化して報告することができます。例えば、npmのアドバイザリにある脆弱性の72%は、Snykのオープンソース脆弱性データベースに最初に追加されています。
#6.ローカルのnpmプロキシを使用する
npm レジストリは、すべての JavaScript デベロッパが利用できるパッケージの最大のコレクションであり、Web デベロッパ向けのオープンソースプロジェクトのほとんどがここを拠点としています。しかし、セキュリティ、デプロイメント、パフォーマンスなどの観点から、異なるニーズがある場合もあります。そんなとき、npmを使えば、別のレジストリに切り替えることができます。
npm install
を実行すると、すべての依存関係を解決するためにメインのレジストリとの通信が自動的に開始されます。別のレジストリを使用したい場合は、それも非常に簡単です。
-
npm set registry
を設定すると、デフォルトのレジストリが設定されます。 - 単一のレジストリを使用するには、引数
--registry
を使用します。
Verdaccioは、設定不要のシンプルで軽量なプライベートレジストリで、以下のように簡単にインストールできます。
$ npm install --global verdaccio
独自のレジストリをホストすることは、これほど簡単ではありませんでした。このツールの最も重要な機能をチェックしてみましょう。
- プライベートパッケージ機能、スコープサポート、パッケージアクセス制御、Webインターフェイスでの認証済みユーザーなどのnpmレジストリ形式をサポートします。
- リモートレジストリをフックする機能と、各依存関係を異なるレジストリにルーティングし、tarballをキャッシュする機能を提供します。重複したダウンロードを減らし、ローカル開発サーバーとCIサーバーの帯域幅を節約するために、すべての依存関係をプロキシする必要があります。
- デフォルトの認証プロバイダとして、htpasswdセキュリティを使用していますが、Gitlab、Bitbucket、LDAPもサポートしています。独自のものを使うこともできます。
- 別のストレージプロバイダーを使用して簡単に拡張できます。
- プロジェクトがDockerに基づいている場合は、公式イメージを使用するのが最善の選択です。
- テスト環境の高速起動が可能で、大きなモノレポプロジェクトのテストに便利です。
実行は、とても簡単です。
$ verdaccio --config /path/config --listen 5000
verdaccioをローカルのプライベートレジストリに使用している場合は、ローカルレジストリへの公開を強制し、デベロッパが誤ってパブリックレジストリに公開してしまうのを防ぐために、パッケージの設定を検討してください。これを実現するには、package.jsonに以下を追加します。
“publishConfig”: {
“registry”: "https://localhost:5000"
}
レジストリが起動しました。さて、パッケージを公開するには、npmコマンドのnpm publish
をを使用するだけで、世界に公開する準備が整います。
#7.責任を持ってセキュリティの脆弱性を開示するる
セキュリティの脆弱性が発見された場合、事前の警告やユーザーが自衛するための適切な緩和策がないまま公表されると、深刻な脅威となる可能性があります。
セキュリティ研究者は、責任ある情報開示プログラムに従うことを推奨します。責任ある情報開示プログラムとは、脆弱性、その影響、および適用可能性を伝えるために、研究者と脆弱性のある資産のベンダーまたは管理者を結びつけることを目的とした一連のプロセスとガイドラインです。脆弱性が正しくトリアージされると、ベンダーと研究者は、セキュリティ問題が公表される前に、影響を受けるユーザにアップグレードの道筋や改善策を提供するために、脆弱性の修正プログラムと公表日を調整します。
セキュリティは非常に重要であるため、後から考えたり、非倫理的に処理したりすることはできません。Snykでは、セキュリティコミュニティを大切にし、オープンソースパッケージのセキュリティ脆弱性を責任を持って公開することで、ユーザーのセキュリティとプライバシーを確保することができると考えています。
Snykのセキュリティ研究チームは、f2e-serverののケースのように、バグバウンティのためにコミュニティと定期的に協力し、何百ものコミュニティからの情報開示を実現しています。また、Snykは、バージニア工科大学のような学術研究者と非常に緊密なパートナーシップを結び、セキュリティの専門知識を提供し、ベンダーやコミュニティのメンテナと調整する能力を持っています。
私たちと協力して、ディスクロージャーのお手伝いをさせてください。
https://snyk.io/vulnerability-disclosureまたは電子メール security@snyk.io で責任あるセキュリティ開示を行ってください。
当社の情報開示方針はこちらをご覧ください。
#8.2FAの有効化
2017年10月、npmは、npmレジストリを使用してクローズドおよびオープンソースのパッケージをホストするデベロッパ向けに、2要素認証(2FA)のサポートを公式に発表しました。
2FAはしばらく前からnpmレジストリでサポートされていたにもかかわらず、ゆっくりと採用されているようで、その一例として、2018年半ばにESLintチームのデベロッパアカウントが盗まれたことで、悪意のあるバージョンのeslintスコープが悪者によって公開されてしまったeslint-scopeインシデントがありました。
** 緊急セキュリティ警告 ** 共有してください。
本日、eslint-scopeのバージョン3.7.2(https://t.co/Gkc9XhDRN6)に、NPMの認証情報を盗む悪質なコードが含まれていることが判明しました。バージョン3.7.2を使用している場合は、今すぐ対策を行ってください。Snyk DB エントリー:https://t.co/dAhhA3cZQP
- スニーク (@snyksec) 2018年7月12日
2FAを有効にすることは、npmのセキュリティのベストプラクティスとして、簡単かつ重要なメリットがあります。レジストリは、ユーザーのアカウントで2FAを有効にするための2つのモードをサポートしています。
- 承認のみ-ユーザーがWebサイトまたはCLIを介してnpmにログインしたとき、またはプロファイル情報の変更などの他の一連のアクションを実行とき。
- 承認と書き込みモード-プロファイルとログインアクション、トークンやパッケージの管理などの書き込みアクション、チームとパッケージの可視性情報のマイナーサポート。
モバイルデバイスにインストールできるGoogle認証などの認証アプリケーションを身に付ければ、すぐに始めることができます。アカウントの2FA拡張保護を開始する簡単な方法の1つは、npmのユーザーインターフェイスを使用することで、簡単に有効にできます。操作する方は、サポートされているnpmクライアントのバージョン(>>5.5.1)を使用すれば、簡単に2FAを有効にすることができます。
$ npm profile enable-2fa auth-and-writes
コマンドラインの指示に従って2FAを有効にし、緊急時の認証コードを保存します。ログインとプロファイルの変更のみに2FAモードを有効にしたい場合は、上記のコードのauth-and-writes
をauth-only
に置き換えてください。
#9.npmの認証トークンを使用
npm CLIを使用してログインするたびに、ユーザーのトークンが生成され、npmレジストリに対してユーザーを認証します。トークンを使用すると、CIおよび自動化された手順(レジストリ上のプライベートモジュールへのアクセスやビルドステップからの新しいバージョンの公開など)中にnpmレジストリ関連のアクションを簡単に実行できます。
トークンは、npmレジストリWebサイト、およびnpmコマンドラインクライアントを使用して管理できます。CLIを使用して、特定のIPv4アドレス範囲に制限された読み取り専用トークンを作成する例は次のとおりです。
npm CLI でログインするたびに、ユーザー用のトークンが生成され、npm レジストリに対して認証されます。トークンを使用すると、CIや自動化された手順(レジストリ上のプライベートモジュールにアクセスしたり、ビルドステップから新しいバージョンを公開するなど)でnpmレジストリ関連のアクションを簡単に実行できます。
トークンは、npmコマンドラインクライアントを使用するだけでなく、npmレジストリのウェブサイトを通じて管理することができます。CLIを使用して、特定のIPv4アドレス範囲に制限された読み取り専用のトークンを作成する例は以下の通りです。
$ npm token create --read-only --cidr=192.0.2.0/24
どのトークンがユーザーのために作成されたかを確認したり、緊急の場合にトークンを取り消したりするには、それぞれ npm token list
や npm token revoke
を使用します。
npmトークンを保護し、露出を最小限に抑えることで、このnpmセキュリティのベストプラクティスに従っていることを確認してください。
#10.モジュールの命名規則とタイポスクワッティング攻撃の理解
モジュールに名前を付けることは、パッケージを作成するときに最初に行うことですが、最終的な名前を定義する前に、npmはパッケージ名が従わなければならないいくつかのルールを定義します。
- 214文字までに制限されています
- ドットまたはアンダースコアで始まる名前は使用できません
- 名前に大文字は使用できません
- 名前の最後にスペースを入れない
- 小文字のみ
- 一部の特殊文字は使用できません:“〜\ '!()*”)'
- . or _で始めることはできません。
- node_modulesやfavicon.icoの使用は禁止されています。
これらのルールを守っていても、npmは新しいパッケージを公開する際に、スコアとパッケージ名がサービスの条件に違反していないかどうかに基づいて、スパム検出メカニズムを使用していることに注意してください。条件に違反している場合、レジストリがリクエストを拒否する可能性があります。
タイポスクワッティング(Typosquatting)とは、ユーザーの誤字脱字などのミスを利用した攻撃のことです。タイポスクワッティングでは、悪意のある人が、既存の人気モジュールによく似た名前の悪意のあるモジュールを npm レジストリに公開することができます。
我々はnpm エコシステムで何十もの悪意のあるパッケージを追跡してきました。それらは PyPi Python レジストリでも確認されています。おそらく、最もよくあるインシデントは、cross-env、event-stream、eslint-scope でした。
タイポスクワッティング攻撃の主なターゲットのひとつは、ユーザーの認証情報です。どのパッケージもグローバル変数process.env
を通じて環境変数にアクセスできるからです。過去の他の例としては、アプリケーションのソースコードに悪意のあるコードのインジェクションをすることを目的とした、デベロッパをターゲットにしたイベントストリームのケースがあります。
最後に、このような攻撃のリスクを軽減するための10のnpmセキュリティベストプラクティスをご紹介します。
- パッケージのインストール手順をターミナルにコピーペーストする際には、特に注意してください。インストールしようとしているパッケージが本当にそのパッケージであることを、ソースコードリポジトリとnpmレジストリで確認してください。
npm info
でパッケージのメタデータを確認して、コントリビュータや最新バージョンに関する詳細情報を得ることもできます。 - パッケージのインストール手順をターミナルにコピーペーストする際には、特に注意してください。インストールしようとしている
日常業務では、npm のログアウトユーザをデフォルトにして、認証情報がアカウントを簡単に危うくする弱点にならないようにします。 - パッケージをインストールする際には、
--ignore-scripts
を追加して、任意のコマンドが実行される危険性を減らします。例:npm install my-malicious-package --ignore-scripts
チートシートを印刷して、いつでも確認できるようにすることをお勧めします。javascriptのデベロッパをはじめ、npmのすべてのユーザーが従うべきnpmのセキュリティのベストプラクティスを思い出せるでしょう。
プルリクエストでnpmの脆弱性を修正
Snykを使用すると、npmの脆弱性を無料で自動的に見つけて修正できます。
######最後まで、読んでいただき、ありがとうございました!
Contents provided by:
Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global