AWS CloudFront+S3で署名付きCookieでプライベートコンテンツを配信する方法
署名付きURLはよくあるパターンですが、署名付きCookieでコンテンツ配信はあまり使用する機会がなかったので今回調べてみました。
CloudFront設定
「Create Distribution」をクリックします。
Webの「Get Started」をクリックします。
「Origin Domain Name」でS3バケットと紐づけます。
「Create a New Identity」とすることでOAI(Origin Access Identity)が自動作成されます。この際に「Yes, Update Bucket Policy」にしておき、対象のS3のバケットポリシーを自動生成されるようにします。
「Create Distribution」をクリックします。赤枠で囲んでいる部分がオリジンアクセスアイデンティティです。
オリジンアクセスアイデンティティ確認
S3のバケットポリシーを確認します。
オリジンアクセスアイデンティティが追加されていることが確認できます。
これでS3経由でアクセスできずにCloudFront経由でしかアクセスできないようになっているはずです。
S3のURL
CloudFrontのURL
アクセスの確認ができました。今度は署名付きCookieもしくは署名付きURLのみアクセス可能に制限します。
CloudFrontのBehaviorsの設定を確認します。Restrict Viewer Accessが「No」になっている場合は「Yes」に変更します。これで署名付きURL or Cookieで有効期限内のものしかアクセスが出来なくなります。
「Self」にチェックを入れると今のAWSアカウントIDのみとなります。
この設定で、再度、CloudFront経由でアクセスしてみます。
正常にアクセスできず、エラー内容が「キーペアIDのcookieが設定されていない」、的な内容に変わっていることが確認できます。
Restrict Bucket AccessとRestrict Viewer Access
Restrict Bucket Accessはオリジンアクセスアイデンティティで設定された権限でアクセス制御を行います。
Restrict Viewer Accessは署名によるアクセス制御を行います。
以下サイトが検証されているので参考ください。
CloudFront-Policy,CloudFront-Signature,CloudFront-Key-Pair-Idクッキーが必要
この3つのクッキーはシステムログイン時にでもヘッダ情報に設定してもらってからCloudFront + S3経由で署名付きCookieでアクセスすることになると思います。
ちなみにAPI Gateway + Lambdaでクッキーを設定する方法は「API Gateway+LambdaでヘッダにSet-Cookieを複数設定する方法」で書きましたが、スマートな方法ではないことは確かです。。
各クッキーに設定する値は以下の通りです。
クッキー | 値 |
---|---|
CloudFront-Policy | base64エンコードされたJSON形式のポリシーステートメント |
CloudFront-Signature | 署名されたbase64エンコードバージョンのJSONポリシーステートメント |
CloudFront-Key-Pair-Id | アクティブなCloudFrontキーペアID |
カスタムポリシーを作成する場合はこの3つが必要になります。署名するためにCloudFrontのキーペア作成も必要です。「AWS アカウントIDでCloudFrontのキーペアを取得する方法」参照ください。
ポリシーステートメントでプライベートコンテンツにワイルドカードが使える
以下、ポリシーステートメントの例です。URLにワイルドカードが使えるので便利です。
*を付けることによって再帰的にポリシーステートメントが効くようになります。(確認済み)
エポックタイムを指定して期間を指定することも可能です。
但し、いくつかカスタムポリシーには注意点があります。
1.Statementは配列になっているが1つだけしか指定できない
2.Resourceを配列にして複数URLを指定することができない
カスタムポリシーの構文が間違っている状態だと、ブラウザでアクセス時にMalformedPolicyエラーが発生します。
policyファイル
{ "Statement": [ { "Resource":"http://d5e3fn38fzw4xx.cloudfront.net/*", "Condition":{ "DateLessThan":{"AWS:EpochTime":1590000000} } } ] }
ポリシーステートメントをbase64エンコードしていくのですが、参考サイトにある通り、cat policy | tr -d "\t\n\r "
でポリシーステートメントの空白文字や改行コードを削除する必要があります。
※Javaで動作確認した際にaws-java-sdk-cloudfrontの1.11.0のSignerUtilsクラスのbuildCustomPolicyメソッドで生成されるカスタムポリシーには空白があるが動作し、問題ないみたいです
base64エンコードされたJSON形式のポリシーステートメント
LinuxやWSLの環境などで以下を実行します。出力結果をCloudFront-Policyに設定します。
cat policy | tr -d "\t\n\r " | openssl base64 | tr -- '+=/' '-_~'
署名されたbase64エンコードバージョンのJSONポリシーステートメント
LinuxやWSLの環境などで以下を実行します。出力結果をCloudFront-Signatureに設定します。
cat policy | tr -d "\t\n\r " | openssl sha1 -sign pk-XXX.pem | openssl base64 | tr -- '+=/' '-_~'
アクティブなCloudFrontキーペアID
キーペアのファイル名(pk-キーペアID.pem)をCloudFront-Key-Pair-Idに設定します。
確認方法
これでcurlコマンドでアクセスできることを確認します。
curl http://xxx.cloudfront.net/index.html -H \ 'CoudFront-Policy=yyy; CloudFront-Signature=zzz; CloudFront-Key-Pair-Id=xyz'
Chromeで確認したかったので、開発者ツールのコンソールタブでdocument.cookieにcookieを設定しても確認することは可能です。
参考サイト
CloudFront+S3で署名付きURLでプライベートコンテンツを配信する
Javaで署名付きCookieを発行する
Javaで署名付きCookieを発行してみます。Javaと.netはpemファイルではなくderファイルで署名する必要があるようです。
今回、derファイルはsecrets managerに登録しています。
secrets managerからderファイルをバイト配列で取得して、署名します。
build.gradleの依存関係は以下追加します。
implementation 'com.amazonaws:aws-java-sdk-cloudfront:1.11.0' implementation 'com.amazonaws:aws-java-sdk-secretsmanager:1.11.415'
CloudFront-Policy、CloudFront-Signature、CloudFront-Key-Pair-Idを出力しているだけですが、Javaソースです。
package jp.co.confrage; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.stream.Stream; import com.amazonaws.regions.Regions; import com.amazonaws.services.cloudfront.CloudFrontCookieSigner; import com.amazonaws.services.cloudfront.CloudFrontCookieSigner.CookiesForCustomPolicy; import com.amazonaws.services.secretsmanager.AWSSecretsManager; import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder; import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; import com.amazonaws.util.DateUtils; public class Library { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException { AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard().withRegion(Regions.AP_NORTHEAST_1).build(); GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest().withSecretId("hogekey"); // シークレットキー名 GetSecretValueResult getSecretValueResult = client.getSecretValue(getSecretValueRequest); byte[] byteArray = new byte[getSecretValueResult.getSecretBinary().remaining()]; getSecretValueResult.getSecretBinary().get(byteArray); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(byteArray); KeyFactory kf = KeyFactory.getInstance("RSA"); RSAPrivateKey privateKey = (RSAPrivateKey) kf.generatePrivate(keySpec); CookiesForCustomPolicy cookies = CloudFrontCookieSigner.getCookiesForCustomPolicy( "https://www.confrage.co.jp/*", privateKey, "APKWER9QJPY6MZHVCERQ", DateUtils.parseISO8601Date("2020-12-31T23:59:59.999Z"), null, "0.0.0.0/0"); String cookiePolicy = cookies.getPolicy().getKey() + "=" + cookies.getPolicy().getValue(); String cookieSignature = cookies.getSignature().getKey() + "=" + cookies.getSignature().getValue(); String cookieKeyPairId = cookies.getKeyPairId().getKey() + "=" + cookies.getKeyPairId().getValue(); String[] strarray = {cookiePolicy, cookieSignature, cookieKeyPairId}; Stream.of(strarray).forEach(System.out::println); } }
標準出力されるbase64されたポリシーなどを確認しようとしたのですが、提供されているJavaクラスのbuildCustomPolicyメソッドに半角スペースが入っているため、コマンドベースの出力結果と異なる為、比較ができません。
署名付きCookieでパス書き換え
CloudFrontのOrigin Pathを使えばパスを書き換えることが出来ます。さらに書き換えたURLに対して署名付きCookieを発行することが可能です。
Origin Pathを使うことにより以下のようなことが実現可能です。
Origin Path |
---|
/dir/aaa/bbb |
ポリシーのリソースに以下を指定します。
Resource |
---|
https://xxx.cloudfront.net/* |
ワイルドカードは再帰的に効きます。以下2つのURLはともに署名付きCookieの対象です。
有効なURL |
---|
https://バケット名.s3-ap-northeast-1.amazonaws.com/dir/aaa/bbb/1.html |
https://バケット名.s3-ap-northeast-1.amazonaws.com/dir/aaa/bbb/ccc/2.html |
ポリシーのResourceにhttps://xxx.cloudfront.net/*を指定すれば、上記URL2つとも署名付きCookieが有効になります。
KHI入社して退社。今はCONFRAGEで正社員です。関西で140-170/80~120万から受け付けております^^
得意技はJS(ES6),Java,AWSの大体のリソースです
コメントはやさしくお願いいたします^^
座右の銘は、「狭き門より入れ」「願わくは、我に七難八苦を与えたまえ」です^^
コメント