Node.jsサーバーではHTTP Strict Transport Security(HSTS)ヘッダーを使いましょう
Raphael Mun
2022年12月29日
0 分で読めます多くのWebサイトやアプリでは、セキュリティ関連のHTTPヘッダーを使用することが一般的になっています。WebサイトはHTTPリクエストとレスポンスの一部としてヘッダーを使い、HTTPプロトコルで送信されるページやデータに関する情報を伝えます。たとえば、コンテンツが圧縮されたzipファイルであることを示すContent-Encodingヘッダーや、リダイレクト先のURLを指定するLocationヘッダーなどがあります。
現代のWebブラウザーがユーザーを保護するために使用する、数多くのセキュリティヘッダーの中でも、重要なものの1つがHTTP Strict Transport Security(HSTS)ヘッダーです。WebサイトがHSTSヘッダーを送信すると、ブラウザーは次回以降のアクセスでHTTPSプロトコルを使用します。
しかし、利便性が高く、実装も簡単で、ほぼすべてのブラウザーがサポートしているにもかかわらず、HSTSヘッダーを含むHTTPレスポンスはモバイルで約25%、デスクトップで28%にすぎません。
HSTSヘッダーについて詳しく解説し、Webセキュリティに与える影響とNode.jsで使用すべき理由を見ていきましょう。さらに、Node.jsサーバーでHSTSを有効にする方法も紹介します。
HSTSを有効にする理由
HTTPS接続が安全なのは、SSL/TLS証明書を使って送受信されるデータパケットを暗号化・復号するためです。これらの証明書は、信頼できる認証局(CA)が検証できます。そのため、ネットワーク通信を傍受した人物が誰であっても、送信されたデータパケットを読み取れるのはユーザーのコンピューターとサーバーだけです。
一般的なHTTP接続ではデータが暗号化されないため、データにアクセスできる人は誰でも内容を読み取れます。意図しない受信者には、ユーザーのデータを宛先サーバーへ中継・転送するサーバーや、公衆Wi-Fiルーター上のネットワーク通信にアクセスするハッカーなどが含まれる可能性があります。
Snykも共同執筆したWeb Almanac 2022レポートの調査結果を見てみましょう。

現在、includeSubdomainディレクティブを含むリクエストへのレスポンスは、モバイルでわずか34%、デスクトップで37%です。
残りの大多数は中間者(MITM)攻撃を受けるリスクがより高く、Node.js WebアプリケーションのWeb認証情報が侵害されるだけでなく、クレジットカード情報やIDの窃取につながる可能性もあります。特に、攻撃者はログイン情報を入力する瞬間にデータを傍受する必要はありません。ブラウザーのCookieにはセッション/アクセストークンが保存・送信されることが多く、攻撃者はセッションハイジャックによってそれらを取得できます。
HSTSの仕組み
HTTPレスポンスヘッダーにHSTSが含まれていると、Webブラウザーは常にサーバーとのHTTPS接続を使用するようになり、最初にHTTPで接続したユーザーも自動的にHTTPSへリダイレクトされます。それ以降、Webアプリケーションやサイトとの接続は暗号化され、安全な状態に保たれます。また、アプリでセキュアCookieを使用できるようになり、MITM攻撃の防止にも役立ちます。
HSTSヘッダーで使用できるパラメーターは3つあります。順に見ていきましょう。
max - age = <expire-time>
この必須パラメーターは、ブラウザーがサイトへのHTTPS接続を記憶する期間を秒単位で指定します。この期間が過ぎると、次回アクセス時にブラウザーは通常どおりサイトを読み込みます。HSTSヘッダーを受信するたびに、ユーザーのブラウザーで有効期限が更新されます。そのため、このパラメーターは無期限に有効な状態を保つことがあります。値を0に設定すれば、HSTSをすぐに無効にすることもできます。
HSTSの設定をテストするときは、300秒(5分)などの短い期間を指定できます。サイトが正しく動作することを確認できたら、31,536,000秒(1年)など、より長い期間に設定しましょう。
includeSubDomains
includeSubDomainsパラメーターは、WebサイトとすべてのサブドメインでHSTSを有効にするようブラウザーに指示するオプションのフラグです。この設定は広範囲に適用されるため、設定する前に、すべてのサブドメインがHTTPSに対応していることを確認してください。
preload
最初の接続では、侵害されたHTTP接続が使用される可能性があります。このリスクに対処するため、Googleは非公式のpreloadパラメーターを運用しています。主要なブラウザーのほとんどがこのセキュリティヘッダーをサポートしており、リスクを大幅に軽減できます。Webサイトでは、HSTSヘッダーにpreloadパラメーターを追加し、ドメインをpreloadサービスに登録できます。ブラウザーはこのリストを参照して、最初の接続でHTTPSを使用できるかどうかを確認します。preloadパラメーターを機能させるには、max-ageを31,536,000秒以上に設定し、includeSubDomainsパラメーターを有効にする必要があります。
preloadサービスにドメインが登録されると、削除リクエストの処理には数か月かかる場合があるので注意してください。ドメインをpreloadに登録する場合は、申請要件と導入に関する推奨事項を確認しましょう。
HSTSでセキュリティを強化する
Node.jsサーバーのコード例を使って、HSTSヘッダーを返す方法を見ていきましょう。この記事で使用するコード例はすべて公開されており、次のGitHubのオープンソースリポジトリで確認できます。
前提条件
このプロジェクトの実行には、以下が必要です。
登録済みのドメイン名
Node.jsがインストールされ、ネットワークポート80と433が利用可能なサーバー
サーバーを指す、ドメインの有効なDNSレコード
SSL/TLS証明書を取得する
HTTPSを有効にするには、まずドメインの証明書と秘密鍵が必要です。まだ持っていない場合は、Let’s Encryptを利用して、次の手順で取得できます。
まず、手順に従ってcertbotをインストールします。Softwareのドロップダウンからotherを選択し、Systemのドロップダウンからサーバーのオペレーティングシステムを選びます。次に、表示される手順に従ってください。

次に、サーバーでポート80と443が開いていることを確認し、サーバーのドメインを指定して次のコマンドを実行します。
コマンドの前にsudoを付け、特権ユーザーとして実行する必要がある場合があります。

次に、certbot certificatesを実行し(必要に応じてsudoを付けて)、証明書と秘密鍵のファイルパスを取得します。

サーバーコードをセットアップする
まず、次のコマンドを実行してサーバー上にプロジェクトフォルダーを作成します。mkdir snyk-demo。
次に、cd snyk-demoを実行してフォルダーに移動します。
続いて、npm init -yを実行してフォルダーをNode.jsプロジェクトとして初期化します。

次に、サーバーのプロジェクト依存関係をインストールします。ExpressまたはFastifyの該当する手順に従ってください。
Express:
Fastify:
ここでは、HTTPとHTTPSを実行する基本的なExpress Node.jsサーバーを作成します。
このフォルダーにindex.jsファイルを作成し、次のNodeモジュールをコードの先頭に追加します。
Express:
Fastify:
次のコードを追加して証明書ファイルを読み込み、certificatePathの値をドメイン証明書のパスに置き換えます。Let’s Encryptのcertbotを使用した場合は、certbot certificatesを実行してファイルパスを取得できます。
Express:
Fastify:
次に、Express/Fastifyアプリを初期化し、基本的なルートを作成します。
次のコードでは3つすべてのHSTSパラメーターを使用していますが、最初はmax-ageだけを使ってもよいでしょう。
オプション1:Helmetミドルウェアを使用する
HSTSを設定する1つ目の方法は、アプリの初期化時にHelmetミドルウェアを使うことです。
Express:
Fastify:
オプション2:ヘッダーを手動で設定する
もう1つの方法は、すべてのレスポンスに適用されるミドルウェアを使って、HSTSヘッダーを手動で設定することです。
Express:
Fastify:
最後に、次のコードでHTTPサーバーとHTTPSサーバーを作成します。
Express:
Fastify:
参考として、ExpressとFastifyのプロジェクトコードリポジトリへのリンクを紹介します。
最後に、node index.jsでサーバーコードを実行し、Webブラウザーでドメインを開きます。
ブラウザーでDevToolsを開き、Networkタブでレスポンスヘッダーを確認します。HSTSヘッダーが指定されていることを確認できます。

次のステップ
HTTP Strict Transport SecurityヘッダーがNode.jsアプリケーションのセキュリティにとって重要な理由や、MITM攻撃の防止にどう役立つか、Node.jsコードでの実装方法を理解したことで、ユーザーのWebアプリケーションを保護するための知識が身につきました。
最後に、もう1つ重要な点があります。本番環境では、HSTSの設定やSSLの提供をNode.jsアプリで行わないでください。代わりに、これらの処理はNGINXやApacheなどの静的ファイルHTTPサーバーに任せましょう。



