📍 冒頭:アプリエンジニアに伝える「AWSの本当の価値」
「AWS って何が便利?」
アプリエンジニアに聞かれた時、なにより最初に答えるのが
🎯 AWS の最大の魅力:IAM による一元化
「すべての AWS リソースを IAM というサービスで制御できるんだよ。
ここまでが前座です。
※🔒 他のクラウドの批評はおいておいて
他のクラウドだと、アプリケーション側で権限チェックを実装する羽目になる。
AWS は アプリケーションロジックと認可を完全に分離 できる。
これが 「シンプルに細かいことができる」 という AWS の真の進化でさ〜(長い話2始まる)
アプリケーションロジックに ACL を埋め込まなくていい
「すべてのAWSリソースをIAMというサービスで制御してもらえる。アプリケーションロジック中の実装にACLを作り込んだりを排除設計できる 」
「ログインした後に、DBにアクセスするなんて制御いれたりするじゃない。あの辺りをAWS側で制御できちゃう仕組みがあるから面倒なセキュリティ制御も排除してもらえるって魅力的でしょう?他のクラウドサービス? AWSはシンプルに細かいことができるのいいんだって〜(長い話が始まる)」
どういうことか?頭の中に置いておいて欲しい絵

1: ログインをする
(APIGateway:認証する:ログイン認可トークンしかないRole自体がないユーザの判断)
2: ユーザ・パスワードなどでログイン成功
(Cognito認可する:CognitoのUserPoolというユーザのトークンとIAMがひもづく)
3: サービスサイトページへ転送
(APIGatewayによりLambdaにアクセスをする直前、Cognito連携によりIAMのRoleを取得判断する)
4: 認可される
(#3に継続してLambdaへのアクセス,Cloudwatchなどへの書き込み,Dynamoへのアクセスが有効なRolemが獲得できる)
5: Lambdaへのアクセス
(この時、セッションで個別認可されたRoleを保持するのでLambdaが起動する、Cloudwatchのログへの書き込みも可能)
6: LambdaがDynamoへのアクセス
(Lambdaが起動した際に、個別認可されたRoleにDynamoへのアクセス制御がPassRoleによってユーザのかわりにLambdaが認可Roleを利用してアクセスできる。)
_※すごい!っておーつきは最初学んだ時に思いました。これJavaSpringでやったらSpringSecurityIntercepteorでVoteとかいろいろつかわないといけなかったから・・
従来のやり方:
# アプリコードに制御が混在
if user.has_permission('read_db'):
result = db.query(...)
else:
raise PermissionError()
AWS のやり方:
# ビジネスロジックだけ
result = db.query(...) # IAM ロールで制御済み
ログイン後の DB アクセス制御を AWS 側でやってくれる
従来: ログイン → ユーザーコード確認 → DB アクセス権限チェック → クエリ実行
AWS: ログイン → IAM ロール取得 → DB アクセス自動制御(アプリは何もしない)
💡 間違ったユーザーが「他の AWS リソースにアクセスする」こと自体を防ぐ
┌─────────────────────────────────────┐
│ ユーザーA がログイン │
├─────────────────────────────────────┤
│ IAM ロール取得 │
│ ├─ DynamoDB Table1 にアクセス OK │
│ ├─ DynamoDB Table2 にアクセス ✗ │
│ ├─ S3 バケット にアクセス ✗ │
│ └─ Lambda 実行 ✗ │
├─────────────────────────────────────┤
│ アプリコードは「こいつ誰?」を気にしない
│ = セキュリティ処理がない = シンプル!
└─────────────────────────────────────┘
メリット:
- アプリケーションにセキュリティ実装が不要
- 権限漏れがない(IAM が一元管理)
- 誰がいつ何をしたか CloudWatch Logs で全記録
- 間違った実装でも AWS 側で防ぐ(多層防御)
※厳密なビジネスロジック的なACL制御は別にしておきます。間違って入ったユーザがAWSの他のリソースへのアクセス自体を制御できるってとても便利だと思いませんか
Part 1: AWS Cognito + IAM PassRole で理解する認可設計
📚 基礎から応用まで
🎯 3ステップで理解する全体像
ユーザー認証 → ロール取得 → リソースアクセス
(Cognito) (IAM) (PassRole)
Cognito で「あなたは誰か」を確認 → IAM ロールで「何ができるか」を決定
1. 通常の認可(ロールなし)
[API Gateway] → Lambda → [問題]
↓
DB接続に固定の認証情報を埋め込み
↓
全ユーザーが同じ権限
問題点:
- 誰がどこにアクセスしたかトレースできない
- 権限制御が粗い
2. Cognito + PassRole で変わること
[ユーザー]
↓
[Cognito認証] → 一意の「Identity ID」を発行
↓
[IAM ロール取得] ← ここが PassRole
↓
[AWS リソース] → ロール情報付きでアクセス
↓
[監査ログ] → 「ユーザーX が DB の行Y を読んだ」が記録される
3. PassRole の実装フロー
ステップA: Cognito Identity Pool を作成
Cognito Identity Pool
├─ 認証ユーザー用ロール(Authenticated Role)
└─ 未認証ユーザー用ロール(Unauthenticated Role)
ステップB: IAM ロールに権限を定義
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query"
],
"Resource": "arn:aws:dynamodb:ap-northeast-1:*:table/UserData",
"Condition": {
"StringEquals": {
"dynamodb:LeadingKeys": "${aws:username}"
}
}
}
]
}
ポイント: ${aws:username} で「その人のデータだけ」に制限
ステップC: Lambda(またはアプリ)が ロール情報を使用
import boto3
import json
from aws_lambda_powertools import logger
logger = logger.Logger()
def lambda_handler(event, context):
# Cognito から発行されたトークンを取得
id_token = event['headers']['Authorization']
# 認証済みユーザー用ロールを AssumeRole
cognito_client = boto3.client('cognito-identity')
credentials = cognito_client.get_credentials_for_identity(
IdentityId=event['requestContext']['authorizer']['claims']['sub']
)
# 一時的な認証情報でDynamoDBアクセス
dynamodb = boto3.resource(
'dynamodb',
aws_access_key_id=credentials['Credentials']['AccessKeyId'],
aws_secret_access_key=credentials['Credentials']['SecretKey'],
aws_session_token=credentials['Credentials']['SessionToken']
)
# ロール権限の範囲でのみアクセス可能
table = dynamodb.Table('UserData')
response = table.get_item(Key={'userId': event['userId']})
logger.info(f"User {event['userId']} accessed their data")
return {
'statusCode': 200,
'body': json.dumps(response['Item'])
}
4. PassRole とは何か?
PassRole = 「ロールを他のAWSサービスに使わせる権限」
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/CognitoAuthRole"
}
]
}
これがないと:
[Cognito] → ロール作成 ✓
→ Lambda に使わせる ✗ (Permission Denied)
5. CloudFormation で一気に構築
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Cognito + IAM PassRole 認可設計'
Resources:
# Cognito Identity Pool
IdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: MyAuthPool
AllowUnauthenticatedIdentities: false
CognitoIdentityProviders:
- ClientId: !Ref UserPoolClient
ProviderName: !GetAtt UserPool.ProviderName
# 認証ユーザー用IAMロール
AuthenticatedRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Federated: cognito-identity.amazonaws.com
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
cognito-identity.amazonaws.com:aud: !Ref IdentityPool
ForAllValues:StringLike:
cognito-identity.amazonaws.com:sub_id: authenticated
Policies:
- PolicyName: DynamoDBUserAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:Query
Resource: arn:aws:dynamodb:ap-northeast-1:*:table/UserData
Condition:
StringEquals:
dynamodb:LeadingKeys: ${aws:username}
# ロール と Identity Pool を関連付け
IdentityPoolRoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
authenticated: !GetAtt AuthenticatedRole.Arn
6. PassRole 利点まとめ
| 項目 | 利点 |
|---|---|
| 監査 | 誰がいつ何をしたかCloudWatch Logsに記録 |
| 最小権限 | ユーザーごと、テーブルごと、行ごとに制御可能 |
| 秘密管理 | 認証情報をアプリに埋め込まない |
| 一時的 | 認証情報は1時間程度で自動失効 |
| OpenSearchも同じ | SigV4署名でOpenSearchもPassRoleで制御可能 |
7. よくある質問
Q: PassRole がないとどうなる?
A: Cognito が Identity を作っても、そのロールをLambdaが使えず、AccessDenied エラー
Q: ユーザーごとに違うロール使いたい場合は?
A: Cognito User Pool の custom:role 属性で ロール名を管理し、Lambda側で動的に AssumeRole
Q: 既存アプリに導入するコストは?
A: CloudFormation テンプレートで15分。あとはアプリ側で AssumeRole の1行追加するだけ
Part 2: API Gateway Authorizer による実装簡素化
🚀 認可処理を API Gateway で一元化
🎯 API Gateway Authorizer による認可フロー
[API Request]
↓
[API Gateway Authorizer Lambda] ← トークン検証 & ロール取得を一元化
↓ (Context に認可情報を埋め込む)
[ビジネスロジック Lambda] ← すでにロール情報がある状態で実行
↓
[DynamoDB] ← Lambda実行ロールで自動アクセス
つまり: ビジネスロジック Lambda は「認可なんて知らない」状態で動作
1. API Gateway Authorizer Lambda(認可処理)
import json
import boto3
from jose import jwt
import time
# Cognito 設定
COGNITO_REGION = 'ap-northeast-1'
USER_POOL_ID = 'ap-northeast-1_xxxxx'
COGNITO_CLIENT_ID = 'xxxxx'
cognito = boto3.client('cognito-idp', region_name=COGNITO_REGION)
def lambda_handler(event, context):
"""
API Gateway からトークンを受け取り、
検証 → Context にロール情報を埋め込んで返す
"""
# API Gateway から Authorization ヘッダーを取得
token = event['authorizationToken']
try:
# トークンを検証
payload = jwt.get_unverified_claims(token)
user_id = payload['sub']
# 検証OKなら、認可情報をContext に埋め込む
return {
'principalId': user_id,
'policyDocument': {
'Version': '2012-10-17',
'Statement': [
{
'Action': 'execute-api:Invoke',
'Effect': 'Allow',
'Resource': event['methodArn']
}
]
},
# ← ここがポイント!
'context': {
'userId': user_id,
'userRole': payload.get('custom:role', 'user'),
'timestamp': str(int(time.time()))
}
}
except Exception as e:
print(f"Auth failed: {e}")
raise Exception('Unauthorized')
2. ビジネスロジック Lambda(簡潔!)
import json
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('UserData')
def lambda_handler(event, context):
"""
API Gateway Authorizer が認可済みのリクエストのみ到達
→ トークン検証コード不要
"""
# Authorizer Lambda から渡されたContext 情報を取得
user_id = event['requestContext']['authorizer']['userId']
user_role = event['requestContext']['authorizer']['userRole']
# ビジネスロジックだけに集中
response = table.get_item(
Key={'userId': user_id}
)
return {
'statusCode': 200,
'body': json.dumps(response.get('Item', {}))
}
比較:
| 従来(Authorizer なし) | Authorizer あり |
|---|---|
| Lambda内でトークン取得 | トークン取得なし |
| jwt検証コード | 検証コード消える |
| AssumeRole呼び出し | 呼び出しなし |
| コード行数:30行 | コード行数:10行 |
3. CloudFormation で接続
Resources:
# Authorizer Lambda
AuthorizerFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: api-authorizer
Runtime: python3.11
Code:
ZipFile: |
# さっきのAuthorizer コード
Role: !GetAtt AuthorizerRole.Arn
# API Gateway に Authorizer をアタッチ
ApiAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
RestApiId: !Ref RestApi
Name: CognitoAuthorizer
Type: TOKEN
IdentitySource: method.request.header.Authorization
FunctionName: !GetAtt AuthorizerFunction.Arn
# API リソース & メソッド
ApiResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt RestApi.RootResourceId
PathPart: users
ApiMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref ApiResource
HttpMethod: GET
AuthorizationType: CUSTOM
AuthorizerId: !Ref ApiAuthorizer # ← ここで Authorizer を指定
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BusinessLogicFunction.Arn}/invocations'
4. 実行フロー図
[クライアント]
↓
GET /users
Authorization: Bearer eyJxxx...
↓
[API Gateway]
├─ Authorizer Lambda が割り込む
│ ├─ トークン検証
│ ├─ Context に userId, userRole を格納
│ └─ Allow policy を返す
↓
[ビジネスロジック Lambda]
├─ event['requestContext']['authorizer']['userId'] で取得
└─ DynamoDB Query
↓
[レスポンス]
5. さらに簡潔:Cognito User Pool Authorizer
ApiAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
RestApiId: !Ref RestApi
Name: CognitoAuthorizer
Type: COGNITO_USER_POOLS # ← カスタム Lambda ではなく Cognito 直接
IdentitySource: method.request.header.Authorization
ProviderARNs:
- !GetAtt UserPool.Arn
この場合:
- Authorizer Lambda さえも不要
- Cognito User Pool が直接検証
- Context には User Pool の属性が自動で渡される
6. パターン選択フローチャート
認可が必要か?
├─ Yes
│ ├─ 複雑な認可ロジック?
│ │ └─ Yes → Authorizer Lambda(カスタム)+ PassRole
│ └─ No (単にトークン検証だけ)
│ └─ Cognito User Pool Authorizer(最もシンプル)
└─ No
└─ API Gateway にプロキシ設定
🎯 最終結論
AWS IAM の本質
「アプリケーションロジックと認可の分離」
これにより:
- 開発が簡単 - セキュリティ実装がない
- テストが簡単 - 権限チェック不要
- 保守が簡単 - ビジネスロジックだけを修正
- セキュアになる - 実装ミスがない
実装コスト比較
| 方式 | 実装時間 | コード行数 | 認可管理 | 推奨度 |
|---|---|---|---|---|
| PassRole のみ | 1-2日 | 50+ | 複雑 | ⭐ |
| API Gateway Authorizer | 1日 | 30 | 中程度 | ⭐⭐⭐ |
次のステップ
API Gateway Authorizer の習得後、さらに高度な認可設計として Bedrock Agents による自動化を検討してください。別途記事で詳しく説明しています。
最後に
「0を1にする」をミッションとするなら、AWS IAM の仕組みを理解して活用することが、もっともシンプルで、セキュアで、スケーラブルな設計につながります。
これがAWSのみんなに知って欲しい「Building and Block」という考え方。プログラマはさまざまなライブラリをつなぎ、組み合わせいろいろ考慮して、挙げ句の果てに徹夜までして頑張って書くことがあります。ただ、この思想を覚えると 「プログラムをかくべきところ」と「AWSの機能を利用して作り込まないことを選択する」っていうことができます。 ちょっと昔のじぶんなら、プログラマとしてなんでもかんでもかけちゃうからそんなもの使わないで書いちまおうという考えでした。ただし、(そのあと、セキュリティの観点、セキュリティおじさんのマウントとマサカリアタック、アプリケーションサービス感の連携、ログの設計、ACLとは、おーつきくん見積もり終わった?えっ?高くない?)なんてことから解放されたのが最も言いたいことでした。
こういうのって、横道外れるけどAWS Practitionarの認定資格を取得する時に設計思想系をまなぶのでとても大事な認定試験だなというのも気付かされます。
アプリエンジニアに「AWS って何が便利?」と聞かれたら、この資料を見せてあげてください。
きっと、AWS の本当の価値を理解してくれます。