本番環境のベストプラクティス: セキュリティ

概要

「本番環境」という用語は、アプリケーションまたはAPIがエンドユーザーまたは消費者に対して一般的に利用可能になるソフトウェアライフサイクルの段階を指します。対照的に、「開発」段階では、まだコードの作成とテストを積極的に行っており、アプリケーションは外部からのアクセスには公開されていません。対応するシステム環境は、それぞれ本番環境と開発環境として知られています。

開発環境と本番環境は通常、異なるように設定されており、要件も大きく異なります。開発環境で問題なくても、本番環境では受け入れられない場合があります。たとえば、開発環境ではデバッグのためにエラーの詳細なログを記録したい場合がありますが、同じ動作が本番環境ではセキュリティ上の懸念となる可能性があります。また、開発環境ではスケーラビリティ、信頼性、パフォーマンスを気にする必要はありませんが、本番環境ではこれらの懸念事項が重要になります。

: Expressにセキュリティ上の脆弱性を発見したと思われる場合は、セキュリティポリシーと手順を参照してください。

本番環境におけるExpressアプリケーションのセキュリティに関するベストプラクティスには以下が含まれます。

非推奨または脆弱なバージョンのExpressを使用しない

Express 2.xおよび3.xは、もはや保守されていません。これらのバージョンのセキュリティとパフォーマンスの問題は修正されません。使用しないでください! バージョン4に移行していない場合は、移行ガイドに従ってください。

また、セキュリティアップデートページにリストされている脆弱なExpressバージョンを使用していないことを確認してください。使用している場合は、安定版リリース (できれば最新版) にアップデートしてください。

TLSを使用する

アプリが機密データを処理または送信する場合は、トランスポート層セキュリティ (TLS) を使用して接続とデータを保護します。この技術は、クライアントからサーバーに送信される前にデータを暗号化するため、一般的な (そして簡単な) ハックの一部を防ぐことができます。AjaxリクエストやPOSTリクエストは目に見えて明らかでなく、ブラウザで「隠されている」ように見えるかもしれませんが、そのネットワークトラフィックはパケットスニッフィング中間者攻撃に対して脆弱です。

セキュアソケットレイヤー(SSL)暗号化についてよくご存知かもしれません。TLSは単にSSLの次の進化版です。つまり、以前にSSLを使用していた場合は、TLSへのアップグレードを検討してください。一般的に、TLSを処理するにはNginxを使用することをお勧めします。Nginx (およびその他のサーバー) でTLSを構成するための優れたリファレンスについては、推奨サーバー構成 (Mozilla Wiki)を参照してください。

また、無料のTLS証明書を取得するための便利なツールは、Let's Encryptです。これは、インターネットセキュリティ研究グループ (ISRG)によって提供される、無料の自動化されたオープンな認証局 (CA) です。

Helmetを使用する

Helmetは、HTTPヘッダーを適切に設定することで、いくつかの有名なWeb脆弱性からアプリを保護するのに役立ちます。

Helmetは、セキュリティ関連のHTTPレスポンスヘッダーを設定する、いくつかの小さなミドルウェア関数のコレクションです。いくつかの例を以下に示します。

Helmetには、ドキュメントWebサイトで詳細を確認できる、他のいくつかのミドルウェア関数が含まれています。

他のモジュールと同様にHelmetをインストールします。

$ npm install --save helmet

次に、コードで使用します。

// ...

const helmet = require('helmet')
app.use(helmet())

// ...

フィンガープリントを削減する

サーバーのフィンガープリントを削減するために、追加の難読化レイヤーを提供することが役立ちます。セキュリティ上の問題ではありませんが、Webサーバーの全体的な態勢を改善する方法は、サーバーで使用されているソフトウェアをフィンガープリントする能力を低減する対策を講じることです。サーバーソフトウェアは、特定のリクエストに対する応答方法のquirksによってフィンガープリントできます。

デフォルトでは、Express.jsはX-Powered-Byレスポンスヘッダーバナーを送信します。これは、app.disable()メソッドを使用して無効にできます。

app.disable('x-powered-by')

: X-Powered-By headerを無効にしても、高度な攻撃者がアプリがExpressを実行していると判断することを防ぐことはできません。カジュアルな悪用を思いとどまらせる可能性がありますが、アプリがExpressを実行していると判断する他の方法があります。

Express.jsは、独自のフォーマットされた404 Not Foundメッセージと独自のフォーマッターエラー応答メッセージも送信します。これらは、独自のNot Foundハンドラーを追加し、独自のエラーハンドラーを作成することで変更できます。

// last app.use calls right before app.listen():

// custom 404
app.use((req, res, next) => {
  res.status(404).send("Sorry can't find that!")
})

// custom error handler
app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

クッキーを安全に使用する

クッキーがアプリを悪用されないようにするには、デフォルトのセッションクッキー名を使用せず、クッキーセキュリティオプションを適切に設定してください。

主なミドルウェアクッキーセッションモジュールは2つあります。

これら2つのモジュールの主な違いは、クッキーセッションデータを保存する方法です。express-sessionミドルウェアは、セッションデータをサーバーに保存します。セッションデータではなく、セッションIDのみをクッキー自体に保存します。デフォルトでは、インメモリストレージを使用しており、本番環境向けに設計されていません。本番環境では、スケーラブルなセッションストアを設定する必要があります。互換性のあるセッションストアのリストを参照してください。

対照的に、cookie-sessionミドルウェアは、クッキーベースのストレージを実装します。セッションキーだけでなく、セッション全体をクッキーにシリアル化します。セッションデータが比較的小さく、(オブジェクトではなく) プリミティブ値として簡単にエンコードされる場合にのみ使用してください。ブラウザはクッキーあたり少なくとも4096バイトをサポートすることになっていますが、制限を超えないようにするには、ドメインあたり4093バイトを超えないようにしてください。また、クッキーデータはクライアントに表示されるため、安全に保つまたは隠す理由がある場合は、express-sessionの方が適している可能性があることに注意してください。

デフォルトのセッションクッキー名を使用すると、アプリが攻撃される可能性があります。発生するセキュリティ上の問題はX-Powered-Byに似ています。潜在的な攻撃者はそれを使用してサーバーをフィンガープリントし、それに応じて攻撃をターゲットにすることができます。

この問題を回避するには、一般的なクッキー名を使用します。たとえば、express-sessionミドルウェアを使用します。

const session = require('express-session')
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 's3Cur3',
  name: 'sessionId'
}))

セキュリティを強化するために、次のクッキーオプションを設定します。

次に、cookie-sessionミドルウェアを使用した例を示します。

const session = require('cookie-session')
const express = require('express')
const app = express()

const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
  name: 'session',
  keys: ['key1', 'key2'],
  cookie: {
    secure: true,
    httpOnly: true,
    domain: 'example.com',
    path: 'foo/bar',
    expires: expiryDate
  }
}))

認証に対するブルートフォース攻撃を防ぐ

プライベートデータをより安全にするために、ログインエンドポイントが保護されていることを確認してください。

シンプルで強力な手法は、2つのメトリックを使用して認証試行をブロックすることです。

  1. 1つ目は、同じユーザー名とIPアドレスによる連続した失敗試行回数です。
  2. 2つ目は、長期間にわたるIPアドレスからの失敗試行回数です。たとえば、IPアドレスが1日に100回の失敗試行を行った場合は、そのIPアドレスをブロックします。

rate-limiter-flexibleパッケージは、この手法を簡単かつ迅速に行うためのツールを提供します。ドキュメントのブルートフォース保護の例を参照してください。

依存関係が安全であることを確認する

npmを使用してアプリケーションの依存関係を管理することは、強力で便利です。ただし、使用するパッケージには、アプリケーションにも影響を与える可能性のある重大なセキュリティ脆弱性が含まれている場合があります。アプリのセキュリティは、依存関係の「最も弱いリンク」と同じくらい強力です。

npm@6以降、npmはすべてのインストールリクエストを自動的にレビューします。また、「npm audit」を使用して依存関係ツリーを分析することもできます。

$ npm audit

より安全を確保したい場合は、Snykを検討してください。

Snykは、コマンドラインツールと、依存関係の既知の脆弱性についてSnykのオープンソース脆弱性データベースに対してアプリケーションをチェックするGithub統合の両方を提供しています。CLIを次のようにインストールします。

$ npm install -g snyk
$ cd your-app

このコマンドを使用して、アプリケーションの脆弱性をテストします。

$ snyk test

その他の既知の脆弱性を回避する

Expressまたはアプリが使用する他のモジュールに影響を与える可能性のあるNode Security ProjectまたはSnykアドバイザリーに注意してください。一般的に、これらのデータベースは、Nodeセキュリティに関する知識とツールに関する優れたリソースです。

最後に、Expressアプリは、他のWebアプリと同様に、さまざまなWebベースの攻撃に対して脆弱になる可能性があります。既知のWeb脆弱性をよく理解し、それらを回避するための予防措置を講じてください。

その他の考慮事項

ここでは、優れたNode.jsセキュリティチェックリストからのいくつかの推奨事項を示します。これらの推奨事項の詳細については、そのブログ投稿を参照してください。