Cloud FrontでHSTS Preloadに対応する


はじめに

こちらは、ZOZOテクノロジーズその2 Advent Calendar 2018 24日目の記事です。

今回は、Lambda@Edgeを使って、CloudFrontにHSTS Preloadを導入してみます。

HSTS Preloadとは

HSTS is 何

HSTS(Hypertext Strict Transport Security)は、Webサーバがもつレスポンスヘッダの一種で、「このサーバで接続するときには必ずHTTPSでアクセスしてね」というメッセージをWebブラウザに伝える働きを持っています。

具体的には Strict-Transport-Security: max-age=31536000; のようにmax-ageをもたせることで、ブラウザにこの規定の時間がすぎるまではHTTPSを強制化するよう働きかけます。

これによって、仮にユーザが初回HTTPで通信しに来た場合(URLバーでhttp://~で来た場合)も次回以降からはデフォルトでHTTPS通信となり、通信のオーバーヘッドが削減できるというメリットがあります。

HSTSのpreload属性

しかし、HSTSは一度サーバに接続していないと有効にならないという問題もあり、これを解決すべく作られたのが、preload属性です。

HSTSのレスポンスヘッダに Strict-Transport-Security: max-age=63072000; includeSubdomains; preload を持たせた状態でGoogleが公開している登録フォームにアクセスすることで、preloadを有効にする準備が整います。

どうなってるのか

ざっくり言うとこんな感じです。

  1. フォームに登録する
  2. preloadの一覧が入ったJSONに自分のドメインがしばらくすると追加される
  3. Chromeなどの主要ブラウザがアップデートされたタイミングで、JSONに自分のドメインが追加され、ブラウザ上でpreloadリストに加わる

こうすることで、主要ブラウザに「あらかじめHTTPS通信を強制するサイト」として、自分のドメインを認識することができるようになります。

CloudFrontでの問題点

CloudFrontでは、残念ながらHTTPヘッダをきれいに扱う仕組みが提供されていません。また、S3にHSTSをpreload付きで渡すのも、現状不可能です。

そこで登場するのが、Lambda@Edgeです。

Lambda@Edgeとは

Lambda@Edgeは、CloudFrontのルール定義にLambda(Node.js)を使えるサービスです。詳細については、弊社テックブログにも書きましたので、そちらをご覧ください。

実際のコード

node.js
'use strict';

exports.handler = (event, context, callback) => {
  const response = event.Records[0].cf.response;
  const headers = response.headers;

  //Set new headers 
  headers['strict-transport-security'] = [{
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000; includeSubdomains; preload'
  }];

  callback(null, response);
};

こちらのコードをオリジンレスポンスに対して仕込んであげることで、オリジンからのコンテンツ返却に合わせた形でHSTSをかぶせることができます。

反映が確認できたら、Googleの登録フォームにて該当ドメインの申請をしてあげましょう。

なお、HSTS preloadではmax-ageに下限値があることと、includeSubdomainsも強制化される点があります。ご注意ください。

反映が完了すると、下記のようにpreloadのリストがすべてのブラウザで有効になることがわかります。なお、こちらの反映にもしばらくの時間を有するため、ご注意ください。