Posted at

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のリストがすべてのブラウザで有効になることがわかります。なお、こちらの反映にもしばらくの時間を有するため、ご注意ください。

Screenshot from 2018-12-24 16-57-02.png