Skip to content

Instantly share code, notes, and snippets.

@m28dev
Last active January 17, 2023 12:58
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m28dev/ea56ba7468996c4bb7180361074d47e7 to your computer and use it in GitHub Desktop.
Save m28dev/ea56ba7468996c4bb7180361074d47e7 to your computer and use it in GitHub Desktop.
Create CloudFront Signed Cookies
const SecretsManager = require('aws-sdk/clients/secretsmanager');
const CloudFront = require('aws-sdk/clients/cloudfront');
const { URL } = require('url');
const responseBodyTemplate = `<!DOCTYPE html>
<html lang="ja">
<meta http-equiv="refresh" content="1; url=###URL###">
<meta charset="utf-8">
<title>redirect..</title>
移動しない場合は<a href="###URL###">こちら</a>をクリックしてください`;
exports.handler = async(event) => {
// Cookieが有効期限内にもかかわらず呼び出された場合は403を返す(主に存在しないコンテンツへのアクセス)
if (event.multiValueHeaders.cookie) {
const requestCookie = event.multiValueHeaders.cookie[0];
const requestPolicy = /CloudFront-Policy=(.*);*/.exec(requestCookie);
if (requestPolicy) {
// カスタムポリシーをデコードして有効期限内か確認する
const decoded = Buffer.from(requestPolicy[1].replace('-', '+').replace('_', '=').replace('~', '/'), 'base64').toString();
const exp = JSON.parse(decoded).Statement[0].Condition.DateLessThan['AWS:EpochTime'];
if (Math.floor(Date.now() / 1000) < exp) return { statusCode: 403 };
}
}
// アクセスを提供するドメインの設定
const distributionDomain = 'example.com';
const distributionSubDomain = 'contents';
// Cookieを発行した後のリダイレクト先を設定
const r = new URL(event.multiValueQueryStringParameters.r[0], 'https://example.com');
const toRidirect = `https://${distributionSubDomain}.${distributionDomain}${r.pathname}`;
// 署名付きCookie用のキーペア ID とプライベートキーを取得
const client = new SecretsManager({ region: 'ap-northeast-1' });
const data = await client.getSecretValue({ SecretId: 'restricted-cf-secret' }).promise();
const secretJson = JSON.parse(data.SecretString);
const keyPairId = secretJson.KEY_PAIR_ID;
const privateKey = secretJson.PRIVATE_KEY.replace(/(-----) /, '$1\n').replace(/ (-----)/, '\n$1'); // --BEGIN と --END の間には改行がないとエラーになる
// 署名付きCookieの生成
const policy = {
Statement: [{
Resource: `https://${distributionSubDomain}.${distributionDomain}/*`,
Condition: {
DateLessThan: { "AWS:EpochTime": Math.floor(Date.now() / 1000) + (60 * 60 * 2) } // 2時間後
}
}]
};
const signer = new CloudFront.Signer(keyPairId, privateKey);
const signed = signer.getSignedCookie({ policy: JSON.stringify(policy) });
// 署名付きCookieを設定したレスポンスを返す
const cookieCfPolicy = `CloudFront-Policy=${signed['CloudFront-Policy']};Domain=${distributionDomain};path=/;Secure;HttpOnly;`;
const cookieCfSignature = `CloudFront-Signature=${signed['CloudFront-Signature']};Domain=${distributionDomain};path=/;Secure;HttpOnly;`;
const cookieCfKeyPairId = `CloudFront-Key-Pair-Id=${signed['CloudFront-Key-Pair-Id']};Domain=${distributionDomain};path=/;Secure;HttpOnly;`;
const response = {
statusCode: 200,
multiValueHeaders: {
'Set-Cookie': [cookieCfPolicy, cookieCfKeyPairId, cookieCfSignature],
'Content-Type': ['text/html'],
},
body: responseBodyTemplate.replace(/###URL###/g, toRidirect)
};
return response;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment