SlideShare a Scribd company logo
AWS で認証機能のついたサイトを手軽に
構築する
Ryosuke Takeuchi
目次
 概要
 システム構成
 処理フロー
 構築手順
1. 静的サイトと API の実装
2. 認証機能の実装
本ドキュメントの目的
• AWS のサービスを利用することで、静的サイト配信や API のホスティング、
認証基盤・ユーザー管理などの機能を簡便に実現できます
背景
解決策
本ドキュメン
トの目的
• 静的サイトを制限されたユーザーに限定して公開したい。また、 HTML を静
的サイトとして配信するだけでなく、 API を実行できるようにしたい
• API には認証機能を実装し、静的サイトからのリクエストのみ受け入れるよ
うにしたい
• AWS のサービスを活用した認証機能つきサイトのシステム構成について説明
します
• システムの例として、外部の認証済ユーザーに対して、サーバにアップロー
ドしたファイル一覧に提示して閲覧できるサイトの構成および構築手順につ
いて説明します
概要
アクセスを許可したユー
ザー
その他のユーザー
ストレー
ジ
API サー
バ
配信
リクエス
ト
ブラウザ
HTML ・
JavaScript
実装するシステムの機能について
概要
• ユーザーがメールアドレスとパスワードでログインできる
• ボタンを押すと、プライベートなバケットに保存したファイルのファイル名を表示する
• ファイル名には一時的なアクセス権を付与した URL をリンクとして設定し、クリックすると
ファイルにアクセスできる
システム全体像 ( 認証機能なし )
システム構成
AWS Cloud
AWS Lambda
パブリックアクセスはブロック
Amazon Simple Storage
Service (Amazon S3)
Amazon API Gateway
Amazon CloudFront
全ての外部ユーザーが
静的サイトにアクセス
可能
全ての外部ユーザー
が API を実行可能
プライベートなファイルに対
して、 Lambda 関数に付与し
たロールで署名した URL を
生成し、静的サイトに表示す
る
静的サイト
API
• HTML ファイルで静的サイトを作成し、 S3 でホスティングする
• S3 には CloudFront 経由でアクセスするように制限する ( 参考 :
Amazon CloudFront オリジンアクセスコントロール(OAC)のご紹介)
• Node.js で実装し Lambda 関数でホスティングする
• S3 のバケットに保存したプライベートなファイルに対して、 Lambda 関数の
実行ロールで署名した URL を生成する API を実行する。それによって一時的
に外部ユーザーにアクセス権を付与する ( 参考 : 署名付きURL
を使用したオブジェクトの共有)
認証機能を追加したシステムの全体像
システム構成
AWS Cloud
AWS Lambda
Amazon Cognito
パブリックアクセスはブロック
Amazon Simple Storage
Service (Amazon S3)
Cognito 認証用
Lambda@Edge
Amazon API Gateway
Amazon CloudFront
API Gateway 認証用
Lambda@Edge
• CloudFront へのアクセス時に Cognito ユーザープールを利用して認証を要求
し、認証されたユーザーのみ静的サイトへのアクセスを許可する
• 認証処理は CloudFront に紐づけた Lambda@Edge で実行する
静的サイト
へのアクセ
ス制限
API へのア
クセス制限
• API Gateway へのアクセス時に Cognito ユーザープールで発行された認証トー
クンを検証し、認証情報が有効な場合に限ってアクセスを許可する。
• 認証トークンは Authorization リクエストヘッダに指定する必要があるため
Lambda@Edge で Cookie から取得してヘッダに設定する
ユーザー管理・認
証情報を提供
CloudFront へ
のアクセス時に
認証処理を実行
Cookie🍪
他の方式との比較
システム構成
AWS Cloud
Lambda
Amazon Cognito
S3 API Gateway
CloudFront
• CloudFront ではなく、 S3 でホストしている静的サイトで Cognito ユーザー
プールに対する認証リクエストと API Gateway 用の Authorization ヘッダの設
定を実行する ( 下図参照 )
方式の概要
Pros
• 認証情報が関わる処理 ( 認証フロー・ Authorization ヘッダの設定 ) が静的サイ
トの中で完結するため、システムの構成がシンプルで設定が簡便
Cons
• Cognito ユーザープールの情報を HTML や JavaScript など記載して公開する必
要があるため、セキュリティリスクがある → 認証処理に関する情報を隠すた
めに、 Lambda @ Edge で認証処理を実装する方式を検討した
Cognito ユーザー
プールの認証情報を
この中で管理
処理フロー (1/2)
処理フロー
AWS Cloud
AWS Lambda
Amazon Cognito
パブリックアクセスはブロック
Amazon Simple Storage
Service (Amazon S3)
Lambda@Edge
Amazon API Gateway
Amazon CloudFront
Lambda@Edge
⑤ 認証情報が無効な場合
は Cognito のホストされ
た UI にリダイレクトし認
証フロー開始
③Cookie🍪 に含ま
れる認証情報を確
認
④ 認証情報が有効な
場合は CloudFront に
遷移
⑥ オリジンに設定したバ
ケットから静的 Web サイ
トを配信
①CloudFront
にアクセス
②Cognito の認証情報を検
証する Lambda@Edge を
実行
Cookie🍪
処理フロー (2/2)
処理フロー
AWS Cloud
AWS Lambda
Amazon Cognito
パブリックアクセスはブロック
Amazon Simple Storage
Service (Amazon S3)
Lambda@Edge
Amazon API Gateway
Amazon CloudFront
Lambda@Edge
Cookie🍪
③ リクエスト
ヘッダのトー
クンを検証
②Cookie か
ら ID トーク
ンを取得し
リクエスト
ヘッダに設
定
④API 実行・署
名付き URL 生
成しレスポン
ス
①HTML から
API 実行
構築手順
1. 静的サイトと API の実装
(1) バケットを作成する
1. 静的サイトと API の実装
• S3 の画面からすべてデフォルト設定でバケットを作成する
• Web サイト用のファイルを格納する pages/ フォルダと、 Web サイトで表示するファイル
を格納する files/ フォルダを作成する
(2) CloudFront でディストリビューションを作成する
1. 静的サイトと API の実装
• CloudFront から “ディストリビューションを作成” に進む
• “ オリジン” セクションで前のステップで作成したバケット名を選択する。オリジンアクセス
で “ Origin access control settings” を選択し、” Create new OAC” からデフォルト設定で OAC
を作成する
(3) OAC のためにバケットポリシーを更新する
1. 静的サイトと API の実装
{
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
“Resource”: “arn:aws:s3:::< バケット名 >static-site-auth-
testing/*",
"Condition": {
"StringEquals": {
“AWS:SourceArn”: "< 作成したディストリビューションの ARN>"
}
}
}
]
}
• バケットの設定画面から “アクセス許可” > “ バケットポリシー” に進み、 CloudFront からア
クセスできるように下記のポリシーを追加する
(4) バックエンド処理用 Lambda 関数を作成する
1. 静的サイトと API の実装
• “S3 オブジェクトの読み取り専用アクセス権限” を選択して新しいロールを作成し、実行ロー
ルとして設定
• Lambda 関数作成後、テストすると 500 エラーが出るので、次項 (6) を参照して権限を更新す
る
(5) バックエンド処理用 Lambda 関数の実行ロールを更新する
1. 静的サイトと API の実装
• Lambda 関数の実行ロールに付与されているポリシーに “ s3:ListBucket” の許可を追加する
(5) バックエンド処理用 Lambda 関数の実行ロールを更新する
1. 静的サイトと API の実装
• Lambda 関数の実行ロールに付与されているポリシーに “ s3:ListBucket” の許可を追加する
(6) バックエンド処理用 Lambda 関数を実装しデプロイ
する
1. 静的サイトと API の実装
• 下記のコードを作成し index.js として保存
const express = require("express");
const awsServerlessExpress = require("aws-serverless-express");
const { S3Client, ListObjectsCommand, GetObjectCommand } = require("@aws-sdk/client-s3");
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
const app = express();
const client = new S3Client({ region: "ap-northeast-1" });
const S3_BUCKET = "webapp-for-file-distribution-20241231";
const PREFIX = "files";
const EXPIRES_IN = 3600;
const listFiles = async () => {
const { Contents } = await client.send(
new ListObjectsCommand({ Bucket: S3_BUCKET, Prefix: PREFIX })
);
return (Contents || [])
.filter((item) => !item.Key.endsWith("/"))
.map((item) => item.Key);
};
const getSignedUrlForFile = (fileKey) =>
getSignedUrl(
client,
new GetObjectCommand({ Bucket: S3_BUCKET, Key: fileKey }),
{ expiresIn: EXPIRES_IN }
);
(6) バックエンド処理用 Lambda 関数を実装しデプロイする
1. 静的サイトと API の実装
• index.js に下記のコードを追加
• ZIP 化して Lambda 関数にデプロイする
app.options("/", (req, res) => res.status(200).send());
app.get("/", async (req, res) => {
try {
const files = await listFiles();
const filesWithUrls = await Promise.all(
files.map(async (file) => ({
fileName: file,
signedUrl: await getSignedUrlForFile(file),
}))
);
res
.status(200)
.json({ message: "Success", files: filesWithUrls });
} catch (err) {
res
.status(500)
.json({ message: "Error", error: err.message });
}
});
const server = awsServerlessExpress.createServer(app);
exports.handler = (event, context) =>
awsServerlessExpress.proxy(server, event, context);
(7) API Gateway を作成
1. 静的サイトと API の実装
• “API Gateway” から “ API を作成” に進み、 API タイプを ” REST API” にして構築する
(8) API Gateway と Lambda を統合
1. 静的サイトと API の実装
• 作成した API リソースから、パス “ /” を選択して “メソッドを作成” をクリック
• 統合タイプは Lambda 関数、 Lambda プロキシ統合にチェックを入れ、デプロイした
Lambda の ARN を入力してメソッドを作成する
(9) API をデプロイする
1. 静的サイトと API の実装
• “ リソース” 画面から “ API をデプロイ” ボタンを押す
• ステージ名を “ dev” としてデプロイ
(10) CloudFront のオリジンに API Gateway を設定する
1. 静的サイトと API の実装
• CloudFront から作成したディストリビューションを選択し、”オリジンを作成” をクリック
• “Origin domain” で前のステップで作成した API Gateway を選択し、すべてデフォルト設定
でオリジンを作成
• “ ビヘイビア” タブから API Gateway 用のビヘイビアを作成する。パスパターンに “ /dev” を
入力して API へのリクエストにマッチングさせる
(11) 静的サイト用 HTML を実装する
1. 静的サイトと API の実装
• 下記のスクリプトを作成し “ index.html” として保存
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<button id="sendRequestButton">Send API Request</button>
<ul id="fileList"></ul>
<script src="script.js"></script>
<script>
document
.getElementById("sendRequestButton")
.addEventListener("click", sendApiRequest);
</script>
</body>
</html>
(12) 静的サイト用 JavaScript を実装する
1. 静的サイトと API の実装
• 下記のスクリプトを作成し “ script.js” として保存
async function sendApiRequest() {
const cfDistributionDomain = “< ディストリビューションのドメイン >";
const apiUrl = `https://${cfDistributionDomain}/dev`;
try {
const response = await fetch(apiUrl, {
method: "GET",
});
const data = await response.json();
displayFilesWithUrls(data.files);
} catch (error) {
console.error("Request failed", error);
alert("Request failed!");
}
}
function displayFilesWithUrls(files) {
const fileListElement = document.getElementById("fileList");
files.forEach((file) => {
const listItem = document.createElement("li");
listItem.innerHTML = `<a href="${file.signedUrl}" target="_blank">${file.fileName}</a>`;
fileListElement.appendChild(listItem);
});
}
(13) 静的サイト用ファイルをバケットにアップロードす
る
1. 静的サイトと API の実装
• “index.html” “script.js” を前のステップで作成したバケットの pages/ にアップロードする
• files/ フォルダにテスト用のファイルをアップロードする
動作確認
1. 静的サイトと API の実装
• < ディストリビューションのドメイン >/pages/index.html にアクセスし、” Send API
Request” ボタンを押すと files/ にアップロードしたファイル名が出力される
• ファイル名をクリックすると、アップロードした画像が表示される
この時点でのシステム構成
1. 静的サイトと API の実装
AWS Cloud
AWS Lambda
Amazon Cognito
パブリックアクセスはブロック
Amazon Simple Storage
Service (Amazon S3)
Cognito 認証用
Lambda@Edge
Amazon API Gateway
Amazon CloudFront
API Gateway 認証用
Lambda@Edge
構築手順
2. 認証機能の実装
(1) Cognito ユーザープールを作成する
2. 認証機能の実装
• “Amazon Cognito” > “ ユーザープール” からユーザープール作成に進む
• サインイン識別子はメールアドレスに設定して ”作成” を押す
• “ ユーザー” タブからテスト用のユーザーを作成する。仮パスワードセクションは ”パスワー
ドの生成” を選択
• ユーザー作成後、入力したメールアドレスに仮パスワードが記載されたメールが届く
(2) Cognito ユーザープールにテスト用ユーザーを作成す
る
2. 認証機能の実装
(2) Cognito ユーザープールにテスト用ユーザーを作成す
る
2. 認証機能の実装
• “ アプリケーションクライアント” から登録したアプリケーションを開き、”ログインページを
表示” をクリックする
• 仮パスワードでログイン後、パスワードを更新してログインページに入れることを確認する
(3) Cognito ユーザープール認証用 Lambda 関数を作成する
2. 認証機能の実装
# プロジェクト作成
$ mkdir lambda-at-edge # 作業用ディレクトリ作成
$ npm init # npm プロジェクト初期化
$ npm install cognito-at-edge # ライブラリインストール
# ビルド
$ npx esbuild --bundle index.js --minify --outfile=bundle.js --platform=node # バンドル
$ zip bundle.zip bundle.js # Lambda にアップロードするために ZIP ファイル作成
# index.js
const { Authenticator } = require("cognito-at-edge");
const authenticator = new Authenticator({
region: "< 認証に使用するユーザープールを作成したリージョン >",
userPoolId: "< ユーザープール ID>",
userPoolAppId: "< クライアント ID>,
userPoolAppSecret: "< クライアントシークレット >",
userPoolDomain: "< ユーザープールのドメイン >",
});
exports.handler = async (request) => authenticator.handle(request);
• npm プロジェクトを作成し “ cognito-at-edge”(GitHub) をインストールする
• index.js を作成しバンドルしてから ZIP ファイルを作成
(3) Cognito ユーザープール認証用 Lambda 関数を作成す
る
2. 認証機能の実装
• バージニア北部 (us-east-1) で関数を作成する
• “ 実行ロール” セクションで、ポリシーテンプレートから ”基本的な Lambda@Edge のアクセ
ス権限” を選択し、新しいロールを作成して設定する
(3) Cognito ユーザープール認証用 Lambda 関数を作成す
る
2. 認証機能の実装
• Lambda 関数の画面から “アップロード元” > “.zip ファイル” を選択し作成した ZIP ファイル
をアップロード
• “ ランタイム設定” からハンドラを “ bundle.handler” に更新する
(4) Cognito ユーザープール認証用 Lambda 関数を Lambda@Ed ge にデプロイする
2. 認証機能の実装
• Lambda 関数のページから “アクション” > “Lambda@Edge へのデプロイ” に進む
(4) Cognito ユーザープール認証用 Lambda 関数を Lambda@Ed ge にデプロイする
2. 認証機能の実装
• キャッシュ動作には “ *” を、“ CloudFront イベント” には ”ビューワーリクエスト” を設定す
る
• “ ボディを含める” “ Lambda@Edge へのデプロイを確認” にチェックを入れて “デプロイ” を押
す
(5) Cognito ユーザープールのコールバック URL を更新
する
2. 認証機能の実装
• Cognito のアプリケーションクライアントから ”ログインページ” タブを開き、”マネージドロ
グインページ” の “許可されているコールバック URL” に CloudFront のドメインを設定する
(6) API Gateway 用の Lambda 関数を作成する
2. 認証機能の実装
• 下記のファイルを index.js として保存し 2(3) の手順を参考にしてビルド・ Lambda 関数を
作成
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const cookies = headers["cookie"] ? headers["cookie"][0].value : "";
const idToken = extractTokenFromCookies(cookies);
headers["authorization"] = [
{
key: "Authorization",
value: `${idToken}`,
},
];
callback(null, request);
};
function extractTokenFromCookies(cookies) {
try {
const match = cookies.match(
/CognitoIdentityServiceProvider.[^.]+.([^.]+).idToken=([^;]+)/
);
if (match && match.length > 2) {
return match[2]; // idToken の値
}
return null;
} catch (error) {
console.error("Error extracting token from cookies:", error);
return null;
}
}
(7) API Gateway 用 Lambda 関数を Lambda@Edge にデプロイする
2. 認証機能の実装
• ディストリビューションの “ビヘイビア” タブからビヘイビアの作成に進む
• パスパターンには “ /< ステージ名 >” を設定する ( 認証をかけたい API にマッチするように
設定すれば OK)
• オリジンには API Gateway のドメインを設定する
• “ オリジンリクエスト” に API Gateway 用に作成した Lambda 関数の ARN を設定する
(8) API Gateway のオーソライザを設定する
2. 認証機能の実装
• 作成した API の “オーソライザー” タブを開きオーソライザーを作成する。オーソライザーの
タイプは Cognito とし、作成したユーザープールを選択する
• 認証をつけたい API のメソッドリクエストを編集し、認可で作成したオーソライザーを設定
する
動作確認 (1)
2. 認証機能の実装
• <CloudFront のドメイン >/<API Gateway のステージ名 > に対してトークンなしで GET メ
ソッドで API を実行すると 401 エラーが返ってくる (= 認証機能が有効化されている )
• Bearer トークンに Cognito ユーザープールの認証時に取得できる ID トークンを設定して実行
すると “ File list with signed URLs retrieved successfully” というメッセージとともに署名付き
URL のリストを取得できる
動作確認 (2)
2. 認証機能の実装
• < ディストリビューションのドメイン >/pages/index.html にアクセスするとログイン画面が
表示される
• 設定したメールアドレスとパスワードを入力するとサイトにアクセスできる
• ”Send API Request” ボタンを押すと files/ にアップロードしたファイル名が出力される
• ファイル名をクリックすると、アップロードした画像が表示される
付録
使用するサービス一覧と機能
付録
サービス名 アイコン 機能
Amazon Simple Storage
Service (Amazon S3)
• 画像、動画などのファイルをクラウド上に保存できる
ストレージサービス
AWS Lambda
• ユーザーからリクエストがあった時だけ起動して処理
を実行する
• Lambda@Edge: ユーザーが CloudFront にアクセスし
た際に処理を実行できる
Amazon CloudFront
• コンテンツ配信サービス。 S3 に保存したファイルを
全世界に高速で配信することができる
• S3 と組み合わせてセキュリティの向上やその他の追
加機能を実装することができる
Amazon API Gateway
• AWS のサービスを API として提供できるサービス
• API に認証を設定できる
Amazon Cognito
• ユーザー管理サービス
• ログイン機能を簡便に追加できる
• パスワード認証や、 Google/Facebook ログインなど
を設定することもできる
本ドキュメントの方式におけるセキュリティリスク
付録
AWS Cloud
AWS Lambda
Amazon Cognito
パブリックアクセスはブロック
Amazon Simple Storage
Service (Amazon S3)
Cognito 認証用
Lambda@Edge
Amazon API Gateway
Amazon CloudFront
API Gateway 認証用
Lambda@Edge
• Cognito ユーザープールから発行された認証情報 ( トークン ) はブラウザの
Cookie に保存される
• Cookie に保存されたトークンが詐取されるリスクがある
リスク
対策
• Cookie の属性に HttpOnly を設定することで、 XSS 攻撃による Cookie の詐取
を防ぐ
• トークンの有効期限を短く設定する
Cookie🍪
CORS エラーが発生した場合
付録
• “ リソース” 画面から “ CORS を有効にする” ボタンを押す
• ゲートウェイのレスポンス、 Access-Control-Allow-Methods にすべてチェックを入れて保存
• Access-Control-Allow-Origin には必要最低限のオリジンを設定する (“*” を設定するとすべて
の CORS が許可されるが、セキュリティリスクがあるので非推奨 )

More Related Content

PPTX
AWSにアップロードしたファイルに認証をつける(CloudFront+Lambda@Edge)
PDF
OPC UA - Information Models & Companion Specifications
PDF
Serverless Microservices Communication with Amazon EventBridge
PPTX
Containerization in microsoft azure
PDF
AZ-500T00 Microsoft Azure Security Technologies.pdf
PDF
AWS Direct Connect 및 VPN을 이용한 클라우드 아키텍쳐 설계:: Steve Seymour :: AWS Summit Seou...
PDF
아마존웹서비스와 함께하는 클라우드 비용 최적화 전략 - 윤석찬 (AWS 코리아 테크에반젤리스트)
PPTX
Azure privatelink
AWSにアップロードしたファイルに認証をつける(CloudFront+Lambda@Edge)
OPC UA - Information Models & Companion Specifications
Serverless Microservices Communication with Amazon EventBridge
Containerization in microsoft azure
AZ-500T00 Microsoft Azure Security Technologies.pdf
AWS Direct Connect 및 VPN을 이용한 클라우드 아키텍쳐 설계:: Steve Seymour :: AWS Summit Seou...
아마존웹서비스와 함께하는 클라우드 비용 최적화 전략 - 윤석찬 (AWS 코리아 테크에반젤리스트)
Azure privatelink

What's hot (20)

PPTX
WSUS移行をしてみた話
PPTX
Azure App Service
PPTX
MS_Azure_Migrate_L300_Refreshed_-_To_be_published.pptx
PPTX
Azure Governance for Enterprise
PPTX
Build, Publish, Deploy and Test Docker images and containers with Jenkins Wor...
PDF
Kubernetes: An Introduction to the Open Source Container Orchestration Platform
PDF
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
PDF
Microsoft azure service 소개자료
PPTX
Azure App Service Deep Dive
PDF
AWS의 다양한 Compute 서비스(EC2, Lambda, ECS, Batch, Elastic Beanstalk)의 특징 이해하기 - 김...
PPTX
2021 AUSG Big Chat - AWS IVS 로 Live Streaming 웹 앱 만들기
PDF
Amazon GameLift – 김성수 (AWS 솔루션즈 아키텍트)
PDF
Digte intranet - Construa sua intranet em poucos cliques fluig®
PDF
Intoduction to VirtualBox English
PDF
AWS Summit Seoul 2023 | 지능화되는 랜섬웨어 위협으로부터 지킬 것인가? 당할 것인가?
PPTX
An introduction to Serverless
PPTX
Microsoft 365 UG Windows Autopilot 1st May 2019
PPT
Chapter 8 Operating Systems And Utility Programs
ODP
Introduction to Amazon Web Services
PPTX
Microsoft Azure - Introduction
WSUS移行をしてみた話
Azure App Service
MS_Azure_Migrate_L300_Refreshed_-_To_be_published.pptx
Azure Governance for Enterprise
Build, Publish, Deploy and Test Docker images and containers with Jenkins Wor...
Kubernetes: An Introduction to the Open Source Container Orchestration Platform
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
Microsoft azure service 소개자료
Azure App Service Deep Dive
AWS의 다양한 Compute 서비스(EC2, Lambda, ECS, Batch, Elastic Beanstalk)의 특징 이해하기 - 김...
2021 AUSG Big Chat - AWS IVS 로 Live Streaming 웹 앱 만들기
Amazon GameLift – 김성수 (AWS 솔루션즈 아키텍트)
Digte intranet - Construa sua intranet em poucos cliques fluig®
Intoduction to VirtualBox English
AWS Summit Seoul 2023 | 지능화되는 랜섬웨어 위협으로부터 지킬 것인가? 당할 것인가?
An introduction to Serverless
Microsoft 365 UG Windows Autopilot 1st May 2019
Chapter 8 Operating Systems And Utility Programs
Introduction to Amazon Web Services
Microsoft Azure - Introduction
Ad

Similar to AWSで認証機能のついたサイトを手軽に構築する(Cognito+CloudFront+API Gateway) (20)

PDF
20180221 AWS Black Belt Online Seminar AWS Lambda@Edge
PDF
AWS Black Belt Online Seminar AWS Amplify
PDF
AWS Lambda ハンズオン 2-Tier アーキテクチャで未来へ
PDF
AWSを利用したアプリ開発
PDF
サーバレスを可能にするAWSサービスの概要
PDF
[AWS Developers Meetup 2017] Developerのための ライブAWSウォークスルー 〜 AWS SDKの使い方 〜
PPTX
サーバレスアプリケーション構築入門
PDF
20190619 AWS Black Belt Online Seminar Dive Deep into AWS Chalice
PDF
AWS Black Belt Techシリーズ AWS Lambda
PDF
AWS Solution Architect Associate試験勉強メモ
PDF
AWS Black Belt Online Seminar AWSサービスを利用したアプリケーション開発を始めよう
PDF
20201028 AWS Black Belt Online Seminar Amazon CloudFront deep dive
PDF
LambdaとMobileの美味しいかもしれない関係
PDF
クラウドネイティブ化する未来
PDF
サーバーレスアーキテクチャのすすめ(公開版)
PPTX
AWSで始めるサーバレスな RESTful API システム
PDF
AWS Black Belt Tech シリーズ 2015 - AWS CodeCommit & AWS CodePipeline & AWS CodeD...
PDF
AWS Lambdaによるサーバレスアーキテクチャの基本に触れてみよう!【kintone & AWS ハンズオン祭り2015秋 B-2】
PDF
AWS Lambdaによるサーバレスアーキテクチャの基本に触れてみよう!【kintone & AWS ハンズオン祭り2015秋 B-2】
PDF
Node.jsアプリの開発をモダン化するために取り組んできたこと
20180221 AWS Black Belt Online Seminar AWS Lambda@Edge
AWS Black Belt Online Seminar AWS Amplify
AWS Lambda ハンズオン 2-Tier アーキテクチャで未来へ
AWSを利用したアプリ開発
サーバレスを可能にするAWSサービスの概要
[AWS Developers Meetup 2017] Developerのための ライブAWSウォークスルー 〜 AWS SDKの使い方 〜
サーバレスアプリケーション構築入門
20190619 AWS Black Belt Online Seminar Dive Deep into AWS Chalice
AWS Black Belt Techシリーズ AWS Lambda
AWS Solution Architect Associate試験勉強メモ
AWS Black Belt Online Seminar AWSサービスを利用したアプリケーション開発を始めよう
20201028 AWS Black Belt Online Seminar Amazon CloudFront deep dive
LambdaとMobileの美味しいかもしれない関係
クラウドネイティブ化する未来
サーバーレスアーキテクチャのすすめ(公開版)
AWSで始めるサーバレスな RESTful API システム
AWS Black Belt Tech シリーズ 2015 - AWS CodeCommit & AWS CodePipeline & AWS CodeD...
AWS Lambdaによるサーバレスアーキテクチャの基本に触れてみよう!【kintone & AWS ハンズオン祭り2015秋 B-2】
AWS Lambdaによるサーバレスアーキテクチャの基本に触れてみよう!【kintone & AWS ハンズオン祭り2015秋 B-2】
Node.jsアプリの開発をモダン化するために取り組んできたこと
Ad

AWSで認証機能のついたサイトを手軽に構築する(Cognito+CloudFront+API Gateway)

  • 2. 目次  概要  システム構成  処理フロー  構築手順 1. 静的サイトと API の実装 2. 認証機能の実装
  • 3. 本ドキュメントの目的 • AWS のサービスを利用することで、静的サイト配信や API のホスティング、 認証基盤・ユーザー管理などの機能を簡便に実現できます 背景 解決策 本ドキュメン トの目的 • 静的サイトを制限されたユーザーに限定して公開したい。また、 HTML を静 的サイトとして配信するだけでなく、 API を実行できるようにしたい • API には認証機能を実装し、静的サイトからのリクエストのみ受け入れるよ うにしたい • AWS のサービスを活用した認証機能つきサイトのシステム構成について説明 します • システムの例として、外部の認証済ユーザーに対して、サーバにアップロー ドしたファイル一覧に提示して閲覧できるサイトの構成および構築手順につ いて説明します 概要 アクセスを許可したユー ザー その他のユーザー ストレー ジ API サー バ 配信 リクエス ト ブラウザ HTML ・ JavaScript
  • 5. システム全体像 ( 認証機能なし ) システム構成 AWS Cloud AWS Lambda パブリックアクセスはブロック Amazon Simple Storage Service (Amazon S3) Amazon API Gateway Amazon CloudFront 全ての外部ユーザーが 静的サイトにアクセス 可能 全ての外部ユーザー が API を実行可能 プライベートなファイルに対 して、 Lambda 関数に付与し たロールで署名した URL を 生成し、静的サイトに表示す る 静的サイト API • HTML ファイルで静的サイトを作成し、 S3 でホスティングする • S3 には CloudFront 経由でアクセスするように制限する ( 参考 : Amazon CloudFront オリジンアクセスコントロール(OAC)のご紹介) • Node.js で実装し Lambda 関数でホスティングする • S3 のバケットに保存したプライベートなファイルに対して、 Lambda 関数の 実行ロールで署名した URL を生成する API を実行する。それによって一時的 に外部ユーザーにアクセス権を付与する ( 参考 : 署名付きURL を使用したオブジェクトの共有)
  • 6. 認証機能を追加したシステムの全体像 システム構成 AWS Cloud AWS Lambda Amazon Cognito パブリックアクセスはブロック Amazon Simple Storage Service (Amazon S3) Cognito 認証用 Lambda@Edge Amazon API Gateway Amazon CloudFront API Gateway 認証用 Lambda@Edge • CloudFront へのアクセス時に Cognito ユーザープールを利用して認証を要求 し、認証されたユーザーのみ静的サイトへのアクセスを許可する • 認証処理は CloudFront に紐づけた Lambda@Edge で実行する 静的サイト へのアクセ ス制限 API へのア クセス制限 • API Gateway へのアクセス時に Cognito ユーザープールで発行された認証トー クンを検証し、認証情報が有効な場合に限ってアクセスを許可する。 • 認証トークンは Authorization リクエストヘッダに指定する必要があるため Lambda@Edge で Cookie から取得してヘッダに設定する ユーザー管理・認 証情報を提供 CloudFront へ のアクセス時に 認証処理を実行 Cookie🍪
  • 7. 他の方式との比較 システム構成 AWS Cloud Lambda Amazon Cognito S3 API Gateway CloudFront • CloudFront ではなく、 S3 でホストしている静的サイトで Cognito ユーザー プールに対する認証リクエストと API Gateway 用の Authorization ヘッダの設 定を実行する ( 下図参照 ) 方式の概要 Pros • 認証情報が関わる処理 ( 認証フロー・ Authorization ヘッダの設定 ) が静的サイ トの中で完結するため、システムの構成がシンプルで設定が簡便 Cons • Cognito ユーザープールの情報を HTML や JavaScript など記載して公開する必 要があるため、セキュリティリスクがある → 認証処理に関する情報を隠すた めに、 Lambda @ Edge で認証処理を実装する方式を検討した Cognito ユーザー プールの認証情報を この中で管理
  • 8. 処理フロー (1/2) 処理フロー AWS Cloud AWS Lambda Amazon Cognito パブリックアクセスはブロック Amazon Simple Storage Service (Amazon S3) Lambda@Edge Amazon API Gateway Amazon CloudFront Lambda@Edge ⑤ 認証情報が無効な場合 は Cognito のホストされ た UI にリダイレクトし認 証フロー開始 ③Cookie🍪 に含ま れる認証情報を確 認 ④ 認証情報が有効な 場合は CloudFront に 遷移 ⑥ オリジンに設定したバ ケットから静的 Web サイ トを配信 ①CloudFront にアクセス ②Cognito の認証情報を検 証する Lambda@Edge を 実行 Cookie🍪
  • 9. 処理フロー (2/2) 処理フロー AWS Cloud AWS Lambda Amazon Cognito パブリックアクセスはブロック Amazon Simple Storage Service (Amazon S3) Lambda@Edge Amazon API Gateway Amazon CloudFront Lambda@Edge Cookie🍪 ③ リクエスト ヘッダのトー クンを検証 ②Cookie か ら ID トーク ンを取得し リクエスト ヘッダに設 定 ④API 実行・署 名付き URL 生 成しレスポン ス ①HTML から API 実行
  • 11. (1) バケットを作成する 1. 静的サイトと API の実装 • S3 の画面からすべてデフォルト設定でバケットを作成する • Web サイト用のファイルを格納する pages/ フォルダと、 Web サイトで表示するファイル を格納する files/ フォルダを作成する
  • 12. (2) CloudFront でディストリビューションを作成する 1. 静的サイトと API の実装 • CloudFront から “ディストリビューションを作成” に進む • “ オリジン” セクションで前のステップで作成したバケット名を選択する。オリジンアクセス で “ Origin access control settings” を選択し、” Create new OAC” からデフォルト設定で OAC を作成する
  • 13. (3) OAC のためにバケットポリシーを更新する 1. 静的サイトと API の実装 { "Statement": [ { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", “Resource”: “arn:aws:s3:::< バケット名 >static-site-auth- testing/*", "Condition": { "StringEquals": { “AWS:SourceArn”: "< 作成したディストリビューションの ARN>" } } } ] } • バケットの設定画面から “アクセス許可” > “ バケットポリシー” に進み、 CloudFront からア クセスできるように下記のポリシーを追加する
  • 14. (4) バックエンド処理用 Lambda 関数を作成する 1. 静的サイトと API の実装 • “S3 オブジェクトの読み取り専用アクセス権限” を選択して新しいロールを作成し、実行ロー ルとして設定 • Lambda 関数作成後、テストすると 500 エラーが出るので、次項 (6) を参照して権限を更新す る
  • 15. (5) バックエンド処理用 Lambda 関数の実行ロールを更新する 1. 静的サイトと API の実装 • Lambda 関数の実行ロールに付与されているポリシーに “ s3:ListBucket” の許可を追加する
  • 16. (5) バックエンド処理用 Lambda 関数の実行ロールを更新する 1. 静的サイトと API の実装 • Lambda 関数の実行ロールに付与されているポリシーに “ s3:ListBucket” の許可を追加する
  • 17. (6) バックエンド処理用 Lambda 関数を実装しデプロイ する 1. 静的サイトと API の実装 • 下記のコードを作成し index.js として保存 const express = require("express"); const awsServerlessExpress = require("aws-serverless-express"); const { S3Client, ListObjectsCommand, GetObjectCommand } = require("@aws-sdk/client-s3"); const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); const app = express(); const client = new S3Client({ region: "ap-northeast-1" }); const S3_BUCKET = "webapp-for-file-distribution-20241231"; const PREFIX = "files"; const EXPIRES_IN = 3600; const listFiles = async () => { const { Contents } = await client.send( new ListObjectsCommand({ Bucket: S3_BUCKET, Prefix: PREFIX }) ); return (Contents || []) .filter((item) => !item.Key.endsWith("/")) .map((item) => item.Key); }; const getSignedUrlForFile = (fileKey) => getSignedUrl( client, new GetObjectCommand({ Bucket: S3_BUCKET, Key: fileKey }), { expiresIn: EXPIRES_IN } );
  • 18. (6) バックエンド処理用 Lambda 関数を実装しデプロイする 1. 静的サイトと API の実装 • index.js に下記のコードを追加 • ZIP 化して Lambda 関数にデプロイする app.options("/", (req, res) => res.status(200).send()); app.get("/", async (req, res) => { try { const files = await listFiles(); const filesWithUrls = await Promise.all( files.map(async (file) => ({ fileName: file, signedUrl: await getSignedUrlForFile(file), })) ); res .status(200) .json({ message: "Success", files: filesWithUrls }); } catch (err) { res .status(500) .json({ message: "Error", error: err.message }); } }); const server = awsServerlessExpress.createServer(app); exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context);
  • 19. (7) API Gateway を作成 1. 静的サイトと API の実装 • “API Gateway” から “ API を作成” に進み、 API タイプを ” REST API” にして構築する
  • 20. (8) API Gateway と Lambda を統合 1. 静的サイトと API の実装 • 作成した API リソースから、パス “ /” を選択して “メソッドを作成” をクリック • 統合タイプは Lambda 関数、 Lambda プロキシ統合にチェックを入れ、デプロイした Lambda の ARN を入力してメソッドを作成する
  • 21. (9) API をデプロイする 1. 静的サイトと API の実装 • “ リソース” 画面から “ API をデプロイ” ボタンを押す • ステージ名を “ dev” としてデプロイ
  • 22. (10) CloudFront のオリジンに API Gateway を設定する 1. 静的サイトと API の実装 • CloudFront から作成したディストリビューションを選択し、”オリジンを作成” をクリック • “Origin domain” で前のステップで作成した API Gateway を選択し、すべてデフォルト設定 でオリジンを作成 • “ ビヘイビア” タブから API Gateway 用のビヘイビアを作成する。パスパターンに “ /dev” を 入力して API へのリクエストにマッチングさせる
  • 23. (11) 静的サイト用 HTML を実装する 1. 静的サイトと API の実装 • 下記のスクリプトを作成し “ index.html” として保存 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Test page</title> </head> <body> <h1>Test page</h1> <button id="sendRequestButton">Send API Request</button> <ul id="fileList"></ul> <script src="script.js"></script> <script> document .getElementById("sendRequestButton") .addEventListener("click", sendApiRequest); </script> </body> </html>
  • 24. (12) 静的サイト用 JavaScript を実装する 1. 静的サイトと API の実装 • 下記のスクリプトを作成し “ script.js” として保存 async function sendApiRequest() { const cfDistributionDomain = “< ディストリビューションのドメイン >"; const apiUrl = `https://${cfDistributionDomain}/dev`; try { const response = await fetch(apiUrl, { method: "GET", }); const data = await response.json(); displayFilesWithUrls(data.files); } catch (error) { console.error("Request failed", error); alert("Request failed!"); } } function displayFilesWithUrls(files) { const fileListElement = document.getElementById("fileList"); files.forEach((file) => { const listItem = document.createElement("li"); listItem.innerHTML = `<a href="${file.signedUrl}" target="_blank">${file.fileName}</a>`; fileListElement.appendChild(listItem); }); }
  • 25. (13) 静的サイト用ファイルをバケットにアップロードす る 1. 静的サイトと API の実装 • “index.html” “script.js” を前のステップで作成したバケットの pages/ にアップロードする • files/ フォルダにテスト用のファイルをアップロードする
  • 26. 動作確認 1. 静的サイトと API の実装 • < ディストリビューションのドメイン >/pages/index.html にアクセスし、” Send API Request” ボタンを押すと files/ にアップロードしたファイル名が出力される • ファイル名をクリックすると、アップロードした画像が表示される
  • 27. この時点でのシステム構成 1. 静的サイトと API の実装 AWS Cloud AWS Lambda Amazon Cognito パブリックアクセスはブロック Amazon Simple Storage Service (Amazon S3) Cognito 認証用 Lambda@Edge Amazon API Gateway Amazon CloudFront API Gateway 認証用 Lambda@Edge
  • 29. (1) Cognito ユーザープールを作成する 2. 認証機能の実装 • “Amazon Cognito” > “ ユーザープール” からユーザープール作成に進む • サインイン識別子はメールアドレスに設定して ”作成” を押す
  • 30. • “ ユーザー” タブからテスト用のユーザーを作成する。仮パスワードセクションは ”パスワー ドの生成” を選択 • ユーザー作成後、入力したメールアドレスに仮パスワードが記載されたメールが届く (2) Cognito ユーザープールにテスト用ユーザーを作成す る 2. 認証機能の実装
  • 31. (2) Cognito ユーザープールにテスト用ユーザーを作成す る 2. 認証機能の実装 • “ アプリケーションクライアント” から登録したアプリケーションを開き、”ログインページを 表示” をクリックする • 仮パスワードでログイン後、パスワードを更新してログインページに入れることを確認する
  • 32. (3) Cognito ユーザープール認証用 Lambda 関数を作成する 2. 認証機能の実装 # プロジェクト作成 $ mkdir lambda-at-edge # 作業用ディレクトリ作成 $ npm init # npm プロジェクト初期化 $ npm install cognito-at-edge # ライブラリインストール # ビルド $ npx esbuild --bundle index.js --minify --outfile=bundle.js --platform=node # バンドル $ zip bundle.zip bundle.js # Lambda にアップロードするために ZIP ファイル作成 # index.js const { Authenticator } = require("cognito-at-edge"); const authenticator = new Authenticator({ region: "< 認証に使用するユーザープールを作成したリージョン >", userPoolId: "< ユーザープール ID>", userPoolAppId: "< クライアント ID>, userPoolAppSecret: "< クライアントシークレット >", userPoolDomain: "< ユーザープールのドメイン >", }); exports.handler = async (request) => authenticator.handle(request); • npm プロジェクトを作成し “ cognito-at-edge”(GitHub) をインストールする • index.js を作成しバンドルしてから ZIP ファイルを作成
  • 33. (3) Cognito ユーザープール認証用 Lambda 関数を作成す る 2. 認証機能の実装 • バージニア北部 (us-east-1) で関数を作成する • “ 実行ロール” セクションで、ポリシーテンプレートから ”基本的な Lambda@Edge のアクセ ス権限” を選択し、新しいロールを作成して設定する
  • 34. (3) Cognito ユーザープール認証用 Lambda 関数を作成す る 2. 認証機能の実装 • Lambda 関数の画面から “アップロード元” > “.zip ファイル” を選択し作成した ZIP ファイル をアップロード • “ ランタイム設定” からハンドラを “ bundle.handler” に更新する
  • 35. (4) Cognito ユーザープール認証用 Lambda 関数を Lambda@Ed ge にデプロイする 2. 認証機能の実装 • Lambda 関数のページから “アクション” > “Lambda@Edge へのデプロイ” に進む
  • 36. (4) Cognito ユーザープール認証用 Lambda 関数を Lambda@Ed ge にデプロイする 2. 認証機能の実装 • キャッシュ動作には “ *” を、“ CloudFront イベント” には ”ビューワーリクエスト” を設定す る • “ ボディを含める” “ Lambda@Edge へのデプロイを確認” にチェックを入れて “デプロイ” を押 す
  • 37. (5) Cognito ユーザープールのコールバック URL を更新 する 2. 認証機能の実装 • Cognito のアプリケーションクライアントから ”ログインページ” タブを開き、”マネージドロ グインページ” の “許可されているコールバック URL” に CloudFront のドメインを設定する
  • 38. (6) API Gateway 用の Lambda 関数を作成する 2. 認証機能の実装 • 下記のファイルを index.js として保存し 2(3) の手順を参考にしてビルド・ Lambda 関数を 作成 exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; const cookies = headers["cookie"] ? headers["cookie"][0].value : ""; const idToken = extractTokenFromCookies(cookies); headers["authorization"] = [ { key: "Authorization", value: `${idToken}`, }, ]; callback(null, request); }; function extractTokenFromCookies(cookies) { try { const match = cookies.match( /CognitoIdentityServiceProvider.[^.]+.([^.]+).idToken=([^;]+)/ ); if (match && match.length > 2) { return match[2]; // idToken の値 } return null; } catch (error) { console.error("Error extracting token from cookies:", error); return null; } }
  • 39. (7) API Gateway 用 Lambda 関数を Lambda@Edge にデプロイする 2. 認証機能の実装 • ディストリビューションの “ビヘイビア” タブからビヘイビアの作成に進む • パスパターンには “ /< ステージ名 >” を設定する ( 認証をかけたい API にマッチするように 設定すれば OK) • オリジンには API Gateway のドメインを設定する • “ オリジンリクエスト” に API Gateway 用に作成した Lambda 関数の ARN を設定する
  • 40. (8) API Gateway のオーソライザを設定する 2. 認証機能の実装 • 作成した API の “オーソライザー” タブを開きオーソライザーを作成する。オーソライザーの タイプは Cognito とし、作成したユーザープールを選択する • 認証をつけたい API のメソッドリクエストを編集し、認可で作成したオーソライザーを設定 する
  • 41. 動作確認 (1) 2. 認証機能の実装 • <CloudFront のドメイン >/<API Gateway のステージ名 > に対してトークンなしで GET メ ソッドで API を実行すると 401 エラーが返ってくる (= 認証機能が有効化されている ) • Bearer トークンに Cognito ユーザープールの認証時に取得できる ID トークンを設定して実行 すると “ File list with signed URLs retrieved successfully” というメッセージとともに署名付き URL のリストを取得できる
  • 42. 動作確認 (2) 2. 認証機能の実装 • < ディストリビューションのドメイン >/pages/index.html にアクセスするとログイン画面が 表示される • 設定したメールアドレスとパスワードを入力するとサイトにアクセスできる • ”Send API Request” ボタンを押すと files/ にアップロードしたファイル名が出力される • ファイル名をクリックすると、アップロードした画像が表示される
  • 44. 使用するサービス一覧と機能 付録 サービス名 アイコン 機能 Amazon Simple Storage Service (Amazon S3) • 画像、動画などのファイルをクラウド上に保存できる ストレージサービス AWS Lambda • ユーザーからリクエストがあった時だけ起動して処理 を実行する • Lambda@Edge: ユーザーが CloudFront にアクセスし た際に処理を実行できる Amazon CloudFront • コンテンツ配信サービス。 S3 に保存したファイルを 全世界に高速で配信することができる • S3 と組み合わせてセキュリティの向上やその他の追 加機能を実装することができる Amazon API Gateway • AWS のサービスを API として提供できるサービス • API に認証を設定できる Amazon Cognito • ユーザー管理サービス • ログイン機能を簡便に追加できる • パスワード認証や、 Google/Facebook ログインなど を設定することもできる
  • 45. 本ドキュメントの方式におけるセキュリティリスク 付録 AWS Cloud AWS Lambda Amazon Cognito パブリックアクセスはブロック Amazon Simple Storage Service (Amazon S3) Cognito 認証用 Lambda@Edge Amazon API Gateway Amazon CloudFront API Gateway 認証用 Lambda@Edge • Cognito ユーザープールから発行された認証情報 ( トークン ) はブラウザの Cookie に保存される • Cookie に保存されたトークンが詐取されるリスクがある リスク 対策 • Cookie の属性に HttpOnly を設定することで、 XSS 攻撃による Cookie の詐取 を防ぐ • トークンの有効期限を短く設定する Cookie🍪
  • 46. CORS エラーが発生した場合 付録 • “ リソース” 画面から “ CORS を有効にする” ボタンを押す • ゲートウェイのレスポンス、 Access-Control-Allow-Methods にすべてチェックを入れて保存 • Access-Control-Allow-Origin には必要最低限のオリジンを設定する (“*” を設定するとすべて の CORS が許可されるが、セキュリティリスクがあるので非推奨 )