Last active
January 17, 2023 12:58
-
-
Save m28dev/ea56ba7468996c4bb7180361074d47e7 to your computer and use it in GitHub Desktop.
Create CloudFront Signed Cookies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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