CloudFront+S3環境でLambda@Edgeを用いてHTTPセキュリティヘッダーを付与する方法

記事タイトルとURLをコピーする

HTTPセキュリティヘッダーとは

「HTTPセキュリティヘッダー」とは、Webブラウザでのセキュリティ対策のために使用されるHTTPヘッダーです。

Webブラウザがセキュリティヘッダーの設定内容に従って動作することで、クロスサイトスクリプティング(XSS)やクリックジャッキング(Clickjacking)などの攻撃を困難にすることができます。(WebブラウザがHTTPセキュリティヘッダーに対応していることが前提になります)

また、nginxやApacheなどのWebサーバーでもこれらのSecurity Headerを付与することができるようになっています。

「HTTPセキュリティヘッダー」の代表的なものとしては以下があります。
(詳細な説明や設定方法についてはリンク先のページをご参考ください)

Strict-Transport-Security

「Strict-Transport-Security」はブラウザに HTTP の代わりに HTTPS を用いて通信をするように指示するヘッダになります。

(参考)Strict-Transport-Security(HSTS)
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Strict-Transport-Security

Content-Security-Policy

コンテンツセキュリティポリシー(CSP)では、Webサイトが読み込む先のURLやスクリプトを指定することができ、クロスサイトスクリプティング(XSS)、クリックジャッキング(Clickjacking)、そのほかのコードインジェクション攻撃を防ぐことができます。

(参考)CSP
https://developer.mozilla.org/ja/docs/Web/HTTP/CSP

X-Content-Type-Options

「X-Content-Type-Options」は「Content-Type」ヘッダーで指定されたMIMEタイプに従うようにするためのヘッダーです。
ブラウザが自動的にMIMEタイプを自動判別して悪意のあるスクリプトを実行してしまうことを防ぎます。

(参考)X-Content-Type-Options
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Content-Type-Options

X-Frame-Options

「X-Frame-Options」は、ブラウザがそのページを他サイトの「iframe」「embed」等の中に表示することを許可するかどうかを指定でき、クリックジャッキング攻撃を防ぐために使用することができます。

(参考)X-Frame-Options
https://developer.mozilla.org/ja/docs/Web/HTTP/X-Frame-Options

X-XSS-Protection

「X-XSS-Protection」は、WebブラウザのXSSフィルター機能を有効化/無効化するためのヘッダーです。
WebブラウザがXSS検出時にページをサニタイズしたり、レンダリングを停止させたりすることができます。

(参考)X-XSS-Protection
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-XSS-Protection

Referrer-Policy

「Referrer-Policy」は、リファラー情報をリクエストにどれだけ含めるかを制御するためのヘッダーです。
「Referrer-Policy」を適切に設定することで、非公開として扱っている遷移元のURLが意図せず漏洩することを防いだりすることができます。

※ 元のヘッダー名の「Referer」は本来は「Referrer」で、スペルが間違っています。
「Referrer-Policy」ヘッダーは正しいスペルになっていることにご注意ください。

(参考)Referrer-Policy
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Referrer-Policy

CloudFront+S3の環境で「HTTPセキュリティヘッダー」は付与できる?

AWSでは静的なWebコンテンツをHTTPSで配信するための構成としてCloudFrontとS3を組み合わせて使うケースがよくあります。

この構成の場合「Lambda@Edge」を使うことでHTTPレスポンスヘッダに「HTTPセキュリティヘッダー」を付与することができます。

Lambda@Edgeを使った「HTTPセキュリティヘッダー」の付与方法

AWSの公式ドキュメントに、Lambda@Edgeを使ったセキュリティヘッダーの付与方法が記載されています。

(参考)Adding HTTP Security Headers Using Lambda@Edge and Amazon CloudFront
https://aws.amazon.com/jp/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/

またLambda@Edgeチュートリアルでは、関数の作成方法のサンプルとして、セキュリティヘッダーを付与するLambda関数が題材となっています。

(参考)チュートリアル: シンプルな Lambda@Edge 関数の作成
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html

実際にやってみた

チュートリアルの内容に従って、Lambda@Edgeを使ったセキュリティヘッダーの付与をやってみたいと思います。

事前に、オリジン用のS3バケットの作成、CloudFront ディストリビューションの作成は済ませてありますので、Lambda@Edge関数の作成からになります。

注意点としては、Lambda@Edge関数は「us-east-1(バージニア北部)」リージョンで作成する必要があります。

Lambdaコンソールに移動後、リージョンが「us-east-1(バージニア北部)」であることを確認し、「関数の作成」ボタンを押します。

「関数の作成」画面では、「設計図の使用」を選択し、キーワードに「cloudfront-modify-response-header」と入力すると、該当のBlueprintが表示されますので、それを選択します。

「基本的な情報」の画面では、

・関数名(任意のファンクション名)
・実行ロール(AWSポリシーテンプレートから新しいロールを作成)
・ロール名(任意のロール名)
・ポリシーテンプレート(Lambda 関数の実行を CloudFront に許可する「基本的な エッジ Lambda のアクセス許可」が自動的に追加されます)

を入力します。

画面を下にスクロールすると「Lambda 関数のコード」が表示されますが、一旦右下にある「関数の作成」ボタンを押します。

次に下記のようなLambda関数の画面にトリガー設定などを行うデザイナー画面が表示されますので、対象のLambda関数名をクリックします。

下にスクロールすると関数のコードが表示されますので、

ランタイムには「Node.js 10.x」を選択します。
※ 2020年2月現在、Lambda@Edgeに利用できるランタイムは「Node.js 10.x」および「Python 3.7」になります。

関数のコードを下記のコードに修正します。

'use strict';
exports.handler = (event, context, callback) => {
    
    //Get contents of response
    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'}]; 
    headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}]; 
    headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}]; 
    headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}]; 
    headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}]; 
    headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}]; 
    
    //Return modified response
    callback(null, response);
};

 

コードの修正が完了したら右上にある「保存」ボタンを押します。

次にCloudFront ボックスをクリックしてハイライトさせた状態で、ページ下部にある「トリガーの設定」で「Lambda@Edge へのデプロイ」ボタンを押します。

「Lambda@Edgeへのデプロイ」というダイアログが表示されますので、下記を設定します。

・ディストリビューション
Lambda@Edgeをデプロイする対象のCloudFrontディストリビューションを選択します

・キャッシュ動作
例では「*」を指定しているので、すべてのリクエストにLambda@Edgeが動作します。
CloudFront側に複数キャッシュ動作を設定している場合は一覧から選択できます。

・CloudFront イベント
「オリジンのレスポンス」を選択します。
これでオリジン(今回はS3)からのレスポンスをトリガーにLambda@Edge関数が動作します。

・「Lambda@Edge へのデプロイを確認」にチェックを入れます。

最後に「デプロイ」ボタンを押します。

Lambda@Edge関数が世界中のエッジロケーションにレプリケートするまで待ちます。
ドキュメントによると「最大で15分かかる場合があります」とのことです。

Lambda@Edge関数のレプリケーションが終了したかどうかを確認するには、CloudFrontコンソールで、ディストリビューションのステータスが「進行中(In Progress)」から「デプロイ済み(Deployed)」になればOKです。

CloudFrontの「Cache Behavior Settings」からも、対象のBehaviorに紐付いているLambda@Edge関数の「Lambda Function ARN」を確認することができます。

注意点としては下記のように、「Lambda Function ARN」は関数のバージョン番号まで含める必要がありますので、関数のバージョンが上がった場合はここも変更する必要があります。

「arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:AddingSecurityHeadersFunction:1

実際にChromeブラウザなどから対象のCloudFrontのURL(https://xxxxxxxxxx.cloudfront.net/index.html)にアクセスしてみます。

下記はChromeブラウザの検証機能で「Response Headers」を表示したものになります。

Lambda@Edgeで設定したセキュリティヘッダが付与されていることが確認できればOKです。

まとめ

CloudFront + S3を使用したWebコンテンツの配信環境においても「Lambda@Edge」を使えば、セキュリティヘッダを付与することができ、よりセキュアにWebコンテンツを配信することができるようになります。

「Lambda@Edge」のその他のユースケースについては下記のページも参考になるかと思います。

(参考)Lambda@Edge 関数の例
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html

http://blog.serverworks.co.jp/tech/2019/03/01/basic-auth/

http://blog.serverworks.co.jp/tech/2018/07/26/lambdaedge-rewrite-response-sorry/