[update] Amazon CloudFrontでCORS access-control headersにワイルドカードを使ったポート指定ができるようになっていました!

Response Headers PoliciesのCORS Access-Control-Allow-Originで「https://example.com:4*3」というようなワイルドカードを使ったポートの指定が可能になりました。
2023.05.17

はじめに

清水です。Amazon CloudFrontの細かなアップデートもしっかりおさえておこうのコーナーです。本エントリではCloudFrontのResponse Header Policiesを利用してCORS access-control headersを追加する際、ポート指定部分にワイルドカードが利用できるようになったアップデートについてお届けします。

本アップデートについてはAWS What's Newでの記載はありませんが、Amazon CloudFront Developer GuideDocument historyを確認すると2023/03/20付で更新があったことが確認できます。

Document history - Amazon CloudFront

CloudFrontのResponse Header Policiesを利用すると、オリジン側の設定変更なしにCloudFront側でResponse Headersを追加することが可能です。ユースケースの一つとして、オリジン側でCORS Headersを指定していない場合に、CloudFront側でResponse Header Policyを用いてCORS Headersを付与する、というものがあります。

この際、Response Header Policy内で各CORS Headersの詳細設定が可能です。特にAccess-Control-Allow-Origin Headerについてはすべてのオリジンを許可する設定(*)のほか、具体的に許可するオリジン(実際のAccess-Control-Allow-OriginのHeaderの値)を指定することが可能です。これまでAccess-Control-Allow-Origin Headerの値にはドメイン名(もしくはワイルドカード*を用いたドメイン名)で指定するかたちでしたが、この度のアップデートでドメイン名に続いてポート番号もワイルドカードを使用するかたちで指定することができるようになりました。

本エントリではこのCORS Access-Control-Allow-Origin Headerにワイルドカードを使ったポート指定ができるようになったアップデートについて、詳細を確認しつつ実際にその挙動を試してみたのでまとめてみたいと思います。

ドキュメントからアップデートの詳細を確認

まずは本アップデートで新たに何ができるようになったのか、その詳細をドキュメントからおさえてみます。Amazon CloudFront Developer GuideDocument history、本アップデートの項目「Added CORS headers wildcard options for ports」のリンク先を確認してみましょう。

Understanding response headers policies」のページの「CORS headers」の項目に遷移します。この中で「Access-Control-Allow-Origin」についての記述を確認すると、Access-Control-Allow-Originレスポンスヘッダを指定する際の例として、ドメインに続くポート部分にワイルドーカード(*)を使用した例が確認できます。

CORS headers - Understanding response headers policies - Amazon CloudFront

Document historyでの更新内容の説明から、この箇所が本アップデートの対象部分に該当しそうです。念のため、Internet Archive Wayback Machineを使って過去のDeveloper Guideの記載と比較してみましょう。該当ページのドキュメントアップデート(2023/03/20)の直前、2023/03/17時点の状態が下記になります。

Understanding response headers policies - Amazon CloudFront (2023/03/17)

ワイルドカードを使ってポートを指定したAccess-Control-Allow-Originレスポンスヘッダについては記載がありませんね。

今回のアップデート、CORS Headersの「Access-Control-Allow-Origin」にて、許可対象となるオリジンを指定する際にポート部分をワイルドカードを使って指定できるようになったものであることが、ドキュメントの更新内容からも確認ができました。

CORS Headersでワイルドカードでポートを指定する検証環境の作成

アップデートの詳細が確認できたところで、実際にResponse Headers PoliciesでAccess-Control-Allow-Origin Headerのワイルドカードを使ったポート指定を行い、動作を確認してみます。

動作検証にあたり、まずは検証環境の作成をしていきましょう。検証に使用するResponse Headers Policyを作成し、ついでこのResponse Headers PolicyをアタッチしたCloudFront Distributionを作成します。

Response Headers Policyの作成

CloudFrontのPolicies画面、Response headersタブの[Create response headers policy]ボタンから進みます。

DetailsのNameとDescriptionを適切に入力し、続いてCross-origin resource sharing (CORS)の項目でConfigure CORSのボタンを有効にします。

CORSに関するResponse Headersについての詳細が現れます。

後ほどいくつかのパターンで動作の検証を行いますが、例えば特定のドメイン(https://cors-origin.example.net)の特定のポート(例として4からはじまり3で終わるポート、4*3)を指定する場合、Access-Control-Allow-Originをデフォルト値のAll originsからCustomizeに変更、「Add origins」が現れるので[Add item]ボタンで項目を追加します。今回の例ではhttps://cors-origin.example.net:4*3という値を追加しました。

その他のCORS Headersについてはデフォルトの設定、その他もデフォルトでResponse Headers Policyを作成します。

CloudFront Distributionの作成

Response Headers Policyが作成できたら、続いてこれを紐付けるCloudFront Distributionを作成します。今回オリジンはS3を利用しました。

Cache key and origin requestsの設定です。動作検証のためキャッシュを行わないCache policy「CachingDisalbed」を選択しました。Response headers policyの項目で先ほど作成したPolicyを選択します。

curlコマンドでリクエスト時のOrigin Headerを指定して動作確認

検証環境の準備ができました。まずはcurlコマンドを用いて、リクエスト時のOrigin Headerの内容ごとに、レスポンスのAccess-Control-Allow-Origin Headerがどうなるかの挙動を確認していきます。

Access-Control-Allow-Originが「All origins」の場合

確認のはじめに今回のアップデートとは直接関係ありませんが、Response Headers PolicyでCORS HeadersのAccess-Control-Allow-Originが「All origins」に設定した場合の挙動をチェックしておきます。以下のように設定した状態ですね。

まずはリクエスト時にOrigin Headerを付与しかなった場合です。Response Headers PolicyでAccess-Control-Allow-Originを「All origins」にしていますが、レスポンス時のでAccess-Control-Allow-Origin Headerは存在しません。これはOriginヘッダが存在しない場合にはAccess-Control-Allow-Origin Headerを返さない、というCORSの仕様通りの実装で、S3などでも同じ挙動となりますね。(参考: [新機能] Amazon CloudFrontがCORSに対応しました | DevelopersIO

% curl -I https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 09:43:30 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 3230xxxxxxxxxxxxxxxxxxxxxxxxcfd4.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-C2
x-amz-cf-id: G25AxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAQ==
vary: Origin

続いてリクエスト時にOrigin Headerを指定したケースです。Response Headers PolicyでAccess-Control-Allow-Originを「All origins」にしてるので、Origin Headerの値は任意でかまいません。実際のレスポンスではAccess-Control-Allow-Origin Headerとして*の値が返ってきました。

% curl -I -H "Origin: https://example.com" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 09:45:41 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 ec75xxxxxxxxxxxxxxxxxxxxxxxx2eea.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P1
x-amz-cf-id: O_M8TxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxA==
access-control-allow-origin: *

Response Headers PolicyでAccess-Control-Allow-Originを「All origins」にしていても、リクエスト時のOrigin HeaderがなければAccess-Control-Allow-Origin Headerが返らないことをおさえておきましょう。

Access-Control-Allow-Originで特定のドメイン名を指定した場合

次にResponse Headers PolicyでCORS HeadersのAccess-Control-Allow-Originに特定のドメイン名を指定した場合の挙動を確認しておきましょう。以下のようにAdd originsでhttps://cors-origin.example.netを設定しました。

リクエスト時のOrigin Headerでドメイン名を指定すると、レスポンスのAccess-Control-Allow-Origin Headerではその指定したドメインが値となって返る、とういうぐあいです。

% curl -I -H "Origin: https://cors-origin.example.net" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 10:43:56 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 ec75xxxxxxxxxxxxxxxxxxxxxxxx2eea.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P1
x-amz-cf-id: OwMexxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGw==
access-control-allow-origin: https://cors-origin.example.net
vary: Origin

さてここで、リクエスト時のOrigin Headerにドメイン名に加えてポート番号を指定してみましょう。任意の(以下の例ではポート番号23516)ポートを付与した場合、CORSは許可されずレスポンスにAccess-Control-Allow-Origin Headerは付与されません。ポート番号443を指定しても同様の結果となりました。

% curl -I -H "Origin: https://cors-origin.example.net:23516" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 10:44:33 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 cc2dxxxxxxxxxxxxxxxxxxxxxxxx09ba.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P1
x-amz-cf-id: v3ofxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx4w==
vary: Origin
% curl -I -H "Origin: https://cors-origin.example.net:443" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 10:44:36 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 c951xxxxxxxxxxxxxxxxxxxxxxxx5f9c.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P1
x-amz-cf-id: zvsZxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxlw==
vary: Origin

Access-Control-Allow-Originで特定ドメイン名のすべてのポートをワイルドカードで指定した場合

続いて今回のアップデート内容の確認です。Response Headers PolicyでAccess-Control-Allow-Originに特定のドメイン名を指定し、すべてのポートをワイルドカードで許可対象としてみます。以下のようにAdd originsで「https://cors-origin.example.net/*」を設定します。

リクエスト時のOrigin Headerでドメイン名に加えてポートを指定してみましょう。レスポンスのAccess-Control-Allow-Origin Headerでは指定したドメインに加え、そのポートも値に含まれています。

% curl -I -H "Origin: https://cors-origin.example.net:23516" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:07:45 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 27cxxxxxxxxxxxxxxxxxxxxxxxx0310a.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-C2
x-amz-cf-id: _6QcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxYg==
access-control-allow-origin: https://cors-origin.example.net:23516
vary: Origin
% curl -I -H "Origin: https://cors-origin.example.net:443" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:07:48 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 f920xxxxxxxxxxxxxxxxxxxxxxxxb87a.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-C2
x-amz-cf-id: nJqAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxYA==
access-control-allow-origin: https://cors-origin.example.net:443
vary: Origin

ポートを指定しなかった場合も確認してみましょう。ポート指定はワイルドカード(*)で行っているので、このポート指定がなくてもCORSが許可される(指定したドメイン名のみのAccess-Control-Allow-Origin Headerが返る)かたちですね。

 % curl -I -H "Origin: https://cors-origin.example.net" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:08:20 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 ac29xxxxxxxxxxxxxxxxxxxxxxxx55ce.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-C2
x-amz-cf-id: JMyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6g==
access-control-allow-origin: https://cors-origin.example.net
vary: Origin

Access-Control-Allow-Originで特定ドメイン名の特定のポートをワイルドカードで指定した場合

続いても今回のアップデート内容の確認、今度はAccess-Control-Allow-Originで特定のポートをワイルドカードを用いて指定してみます。以下のようにAdd originsで「https://cors-origin.example.net:4*3」と設定した場合です。

リクエスト時のOrigin Headerでのポート指定の際、4*3というポートを指定しているので4ではじまり3で終わるポート番号であればCORSが許可され、そのポート番号とドメインがAccess-Control-Allow-Origin Headerの値となります。

 % curl -I -H "Origin: https://cors-origin.example.net:44433" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:16:30 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 0e18xxxxxxxxxxxxxxxxxxxxxxxxa2fe.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-C2
x-amz-cf-id: m7Uwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5A==
access-control-allow-origin: https://cors-origin.example.net:44433
vary: Origin
 % curl -I -H "Origin: https://cors-origin.example.net:443" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:16:23 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 2186xxxxxxxxxxxxxxxxxxxxxxxx4132.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-C2
x-amz-cf-id: Q1pqxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6w==
access-control-allow-origin: https://cors-origin.example.net:443
vary: Origin

ポート番号が4ではじまり3で終わるではない場合、またポート番号を指定しない場合はCORSが許可されず、レスポンス時のAccess-Control-Allow-Origin Headerは返りません。

 % curl -I -H "Origin: https://cors-origin.example.net:23516" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:17:50 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 b125xxxxxxxxxxxxxxxxxxxxxxxxaf62.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-C4
x-amz-cf-id: aQphxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxeQ==
vary: Origin
% curl -I -H "Origin: https://cors-origin.example.net" https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8
HTTP/2 200
content-type: application/x-mpegURL
content-length: 204
date: Tue, 16 May 2023 11:17:55 GMT
last-modified: Sat, 29 Feb 2020 12:33:56 GMT
etag: "1622xxxxxxxxxxxxxxxxxxxxxxxx0778"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 4004xxxxxxxxxxxxxxxxxxxxxxxx96f4.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-C4
x-amz-cf-id: 9ob4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxew==
vary: Origin

Video.jsを用いた特定のポートでホストされたオリジンからのクロスオリジンアクセスの確認

続いて具体的にCORSを用いたリソースへのアクセスの例として、Video.jsを用いた動画再生を行ってみます。(参考 Video.jsでのHLS形式動画の再生を通してS3とCloudFrontのCORS設定について確認してみた | DevelopersIO

特定のポートをワイルドカードで指定したResponse Header Policyを準備、これをアタッチしたS3オリジンのCloudFront DistributionでHLS形式の動画ファイル一式をホスティングします。Video.jsはhttps://cors-origin.example.net:44433、ポート番号444333番でホスティングしているhtmlから呼び出すことします。この環境にALB+EC2を準備しました。EC2上ではApache HTTP ServerがHTTPデフォルトの80番で動作していますが、ALB側でHTTPS処理に加えてポート番号を44433番となるよう設定します。

構成の概要としては以下のようになります。

CloudFront側の設定

CloudFront側は先ほど作成したResponse Headers Policy、ならびにこれを紐付けたDistributionを使用します。Response Headers PolicyのCORS Access-Control-Allow-Originについては「https://cors-origin.example.net:4*3」を設定しておきました。

オリジンとなるS3にはHLS形式の動画ファイル一式が格納されている状況です。HLSマニフェストファイルへのパスはhls/sample59.m3u8となります。(先ほどのcurlコマンドでの確認時、このファイルにアクセスしていましたが、これを起点としてHLSファイルが存在する状態です。)

HTMLをホスティングするALBの設定

ALBについては、通常と異なるポート44433でホスティングHTTPSのホスティングを行います。このための設定についてまとめて負います。

Listeners and routingsの項目、Protocolとして「HTTPS」を選択するとデフォルトでPortは「443」となりますが、ここでPortを「44433」に変更します。Default actionは通常の443番ポート利用時と変わらず、HTTP80番ポートで設定したTarget Groupを選択します。Secure listener settingsの設定項目についても通常の443番ポート利用時と代わりありません、ACMから利用するドメインに対応した証明書を選択しました。

またALBにアタッチするSecurity Groupでは、ポート44433に対してアクセスが許可されるよう設定をしておきます。

ALBが作成され準備ができたら、Route 53でレコードを登録してcors-origin.example.netでアクセスできるようにします。

実際にVideo.jsでのクロスオリジンアクセスを確認

ALB背後のEC2に以下のコードを配置しました。https://cors-origin.example.net:44433/amazon-cloudfornt-added-cors-headers-wildcard-options-for-ports/videojs.htmlでHTMLが参照できるぐあいですね。

amazon-cloudfornt-added-cors-headers-wildcard-options-for-ports/videojs.html

<html>
  <head>
    <title>VHS de HLS</title>
    <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
  </head>
  <body>
    <video-js id=example-video width=1280 height=720
              class="vjs-default-skin" controls>
      <source
         src="https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8"
         type="application/x-mpegURL">
    </video-js>
    <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
    <script>
      var player = videojs('example-video');
    </script>
  </body>
</html>

実際にこのURLにアクセスしてみましょう。問題なくVideo.jsによるHLS動画の再生ができました。確認のためChromeブラウザの開発者ツール、Consoleの部分も見てみますが、favicon.icoが未設定であるエラーのみが表示されている状態です。

ポートを指定しないかたちも確認

Response Headers PolicyのCORS Access-Control-Allow-Originでワイルドカードを使って該当ポートを指定することで、Video.jsによるクロスオリジンアクセスが許可されることが確認できました。試しに、Response HeadersPolicyを以下のようにポートを指定しないかたちに変更してみます。

ページをリロードしてみましょう。(なお通常のリロードではブラウザキャッシュが使用されたため、「ハード再読み込み」を行いました。)Video.jsのPlayer上に「The media could not be loaded, either because the server or network failed or because the format is not supported.」というエラーが表示され、再生がはじまりません。開発者ツールのConsoleを確認してみると、「Access to XMLHttpRequest at 'https://ddz4xxxxxxxxx.cloudfront.net/hls/sample59.m3u8' from origin 'https://cors-origin.example.net:44433' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.」というCORSポリシーエラーが発生していました。

まとめ

CloudFrontのResponse Header Policiesを利用したCORS Headersの設定で、ポート番号についてワイルドカードを使って指定できるようになったアップデートについてお届けしました。クロスオリジンのアクセス元となる環境が通常のポート(HTTPSなら443番、HTTPなら80番)を使用しておらず、また事情により可変になる可能性がある、などといった場合に、無闇矢鱈に「All origins」で制限なく許可を行うのではなく、ワイルドカードを使ったポート指定を行い必要最低限の許可設定を行うようにしましょう。