📚 目次
1結論ファースト
2マンション管理のたとえ
3混乱した代理問題
4解決策:ExternalId
5技術詳細・条件キー比較
6実装例(IAM/CFn/Terraform)
7実際のSaaSサービス例
8トラブルシューティング
9CloudTrail監査方法
10ベストプラクティス
11設定チェックリスト
12FAQ・まとめ
📌 1. 結論ファースト:ExternalIdとは何か?
秘密の合言葉
サードパーティサービスがあなたのAWSアカウントにアクセスする際、「本当にあなたからの依頼か」を確認するための秘密のコード
なりすまし防止
悪意のある第三者が「他人のふりをして」あなたのアカウントにアクセスすることを完全にブロック
必須の場面
SaaS連携、外部ベンダーアクセス、マルチテナント環境などサードパーティとの連携時は必須
🎯 一言で言うと
「IAMロールARNを知っているだけではアクセスできない」ようにする追加の本人確認コード
🏢 2. マンション管理で完全理解
🔑 クロスアカウントアクセス = マンション管理会社への鍵の預け
📊 登場人物と関係図
あなた(マンション所有者)
= AWSアカウント所有者
🔑 マスターキー預け
🔐 合言葉も設定
管理会社
= SaaSサービス(複数顧客を管理)
悪意のある住人B
= 攻撃者(同じSaaSの別顧客)
❌ Aさんの部屋番号だけ伝える
❌ 合言葉を知らない
マンションのドア
= AWS STS(アクセス制御)
🔄 たとえ話 ↔ AWS の対応表
🏠 あなたのマンション
=
☁️ あなたのAWSアカウント
🏢 管理会社(複数の住人を管理)
=
🔧 SaaSサービス(マルチテナント)
🔑 マスターキー
=
🎭 IAMロール(AssumeRole権限)
🚪 部屋番号(101号室)
=
📋 IAMロールARN
🔐 合言葉(秘密のパスワード)
=
🔐 ExternalId
😈 3. 「混乱した代理」問題とは?
⚠️ ExternalIdがないと何が起こる?
攻撃者が「他人のふりをして」あなたのリソースにアクセスできてしまう!
🎬
攻撃シナリオ:時系列で見る「混乱した代理」
正規顧客A
(被害者)
(被害者)
悪意の顧客B
(攻撃者)
(攻撃者)
SaaSサービス
(代理人)
(代理人)
AWS STS
(認証)
(認証)
1
顧客A: SaaSに登録、IAMロールARN
arn:aws:iam::111111:role/ServiceRole を設定
2
顧客A: IAMロールの信頼ポリシーでSaaSアカウントを許可(ExternalIdなし)
3
攻撃者B: 同じSaaSに登録、AさんのARNを自分の設定に入力!
4
攻撃者B: SaaSに「自分のAWS環境でジョブを実行して」と依頼
5
SaaS: Bの設定にあるARN(実はAのもの)でAssumeRole実行
6
AWS STS: ARNとPrincipalが一致 → アクセス許可!(ExternalIdチェックなし)
結果:攻撃者BがAさんのAWSリソースにアクセス成功!
SaaSは「混乱した代理」となり、Bからの依頼を「Aからの正当な依頼」と誤認。
データ窃取、リソース破壊、不正課金などの被害が発生する可能性あり。
データ窃取、リソース破壊、不正課金などの被害が発生する可能性あり。
🎭 なぜ「混乱した代理」と呼ばれるのか?
SaaSサービス(代理人)は、顧客Bから「Aさんのアカウントにアクセスして」と頼まれても、
それが本当にAさん本人からの依頼かどうか区別できない(混乱している)。
結果として、SaaSは善意で動いているのに、攻撃者の共犯者になってしまう。
✅ 4. 解決策:ExternalIdの導入
🛡️ ExternalIdで「なりすまし」を完全ブロック!
📋 ExternalId導入の流れ
1
🏢
SaaSが顧客ごとに固有のExternalIdを発行
SaaSサービスに登録すると、あなた専用のExternalIdが発行されます。
このIDは推測困難なランダム文字列で、顧客ごとに異なります。
このIDは推測困難なランダム文字列で、顧客ごとに異なります。
あなたのExternalId: customer-a-x8K9mP2nL5vQ
2
👤
顧客がIAMロールの信頼ポリシーにExternalIdを設定
発行されたExternalIdを、IAMロールの信頼ポリシーにConditionとして追加します。
これにより「このExternalIdを提示した場合のみアクセス許可」となります。
これにより「このExternalIdを提示した場合のみアクセス許可」となります。
"Condition": { "StringEquals": { "sts:ExternalId": "customer-a-x8K9mP2nL5vQ" } }
3
🔐
SaaSがAssumeRole時にExternalIdを提示
SaaSがあなたのアカウントにアクセスする際、登録済みのExternalIdを一緒に送信します。
AWS STSは信頼ポリシーのConditionと照合し、一致する場合のみアクセスを許可します。
AWS STSは信頼ポリシーのConditionと照合し、一致する場合のみアクセスを許可します。
aws sts assume-role --role-arn "..." --external-id "customer-a-x8K9mP2nL5vQ"
✓
🛡️
攻撃者Bはアクセス不可能!
攻撃者BはAさんのExternalIdを知りません。
BのExternalId(customer-b-yZ3qR7...)とAさんの信頼ポリシーが一致しないため、
アクセスは即座に拒否されます!
BのExternalId(customer-b-yZ3qR7...)とAさんの信頼ポリシーが一致しないため、
アクセスは即座に拒否されます!
正規ユーザーA
SaaSが正しいExternalIdを提示
→ アクセス許可 ✓
→ アクセス許可 ✓
攻撃者B
Bの ExternalIdはAのポリシーと不一致
→ アクセス拒否 ✗
→ アクセス拒否 ✗
⚙️ 5. 技術詳細:STS条件キーの比較
🔧 STSで使用できる条件キーの違い
sts:ExternalId
目的:サードパーティサービスとの連携時に「混乱した代理」問題を防止
📌 使用場面
• SaaS/外部ベンダーとの連携
• マルチテナント環境での分離
• クロスアカウントの本人確認
• マルチテナント環境での分離
• クロスアカウントの本人確認
sts:SourceIdentity
目的:セッションに人間が読める識別子を付与し、監査ログでの追跡を容易にする
📌 使用場面
• ユーザー別の操作追跡
• CloudTrailログの可読性向上
• ロールチェーン時のID保持
• CloudTrailログの可読性向上
• ロールチェーン時のID保持
aws:SourceAccount
目的:リクエスト元のAWSアカウントIDを検証する
📌 使用場面
• AWSサービス間の連携
• S3イベント通知の制限
• Lambda呼び出し元の制限
• S3イベント通知の制限
• Lambda呼び出し元の制限
🎯 ExternalIdと他の条件キーの組み合わせ
ExternalIdは単独でも強力ですが、aws:PrincipalArnやaws:SourceAccountと
組み合わせることで、より堅牢なアクセス制御を実現できます。
多層防御(Defense in Depth)の考え方が重要です。
💻 6. 実装例
📝 各種フォーマットでの実装
IAM Policy (JSON)
CloudFormation
Terraform
AWS CLI
📄 IAM信頼ポリシー(trust-policy.json)
// ExternalIdを必須にした信頼ポリシー { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { // SaaSサービスのAWSアカウント "AWS": "arn:aws:iam::999999999999:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { // ★ ここがポイント!ExternalIdの検証 "sts:ExternalId": "a1b2c3d4-unique-secret-12345" } } } ] }
📄 CloudFormation テンプレート
AWSTemplateFormatVersion: '2010-09-09' Description: 'ExternalId付きクロスアカウントロール' Parameters: ExternalId: Type: String Description: 'SaaSサービスから提供されたExternalId' NoEcho: true # 機密情報として扱う Resources: CrossAccountRole: Type: 'AWS::IAM::Role' Properties: RoleName: 'SaaSServiceRole' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: AWS: 'arn:aws:iam::999999999999:root' Action: 'sts:AssumeRole' Condition: StringEquals: sts:ExternalId: !Ref ExternalId
📄 Terraform
variable "external_id" { description = "SaaSサービスから提供されたExternalId" type = string sensitive = true # 機密情報として扱う } resource "aws_iam_role" "saas_service_role" { name = "SaaSServiceRole" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { AWS = "arn:aws:iam::999999999999:root" } Action = "sts:AssumeRole" Condition = { StringEquals = { "sts:ExternalId" = var.external_id } } } ] }) }
🔧 AWS CLI / boto3 呼び出し例
# AWS CLI aws sts assume-role \ --role-arn "arn:aws:iam::111111111111:role/SaaSServiceRole" \ --role-session-name "SaaSSession" \ --external-id "a1b2c3d4-unique-secret-12345" # Python (boto3) import boto3 sts_client = boto3.client('sts') response = sts_client.assume_role( RoleArn='arn:aws:iam::111111111111:role/SaaSServiceRole', RoleSessionName='SaaSSession', ExternalId='a1b2c3d4-unique-secret-12345' # 必須! ) credentials = response['Credentials']
🏢 7. 実際のSaaSサービス例
📱 ExternalIdを使用する主要SaaSサービス
Datadog
監視・オブザーバビリティ
AWS環境のメトリクス収集、ログ分析、APMのために、S3、CloudWatch、Lambda等にアクセス。
🔄 連携フロー
1
Datadogコンソールで「AWS Integration」を設定
2
自動生成されたExternalIdをコピー
3
CFnテンプレート or 手動でIAMロール作成
New Relic
パフォーマンス監視
インフラストラクチャ監視、APM、ログ管理のためにAWSリソースの情報を収集。
🔄 連携フロー
1
New Relicで「AWS Integrations」を追加
2
アカウント固有のExternalIdが表示
3
提供されるCFnスタックをデプロイ
CloudHealth
コスト管理・最適化
AWSコストの可視化、リソース最適化、ガバナンス機能を提供。
🔄 連携フロー
1
CloudHealthで「AWS Account」を追加
2
自動生成のExternalIdと手順書を取得
3
読み取り専用IAMロールを作成
Prisma Cloud
クラウドセキュリティ
クラウド環境のセキュリティ監視、コンプライアンスチェック、脅威検出。
🔄 連携フロー
1
Prisma Cloudで「Cloud Account」を追加
2
自動生成CFnテンプレートをダウンロード
3
ExternalId込みのロールが自動作成
🔧 8. トラブルシューティング
⚠️ よくあるエラーと解決方法
❌
エラー1: AccessDenied - ExternalId不一致
🔍 原因
- AssumeRole時に指定したExternalIdが、信頼ポリシーのConditionと一致しない
- ExternalIdを指定し忘れている
- ExternalIdにタイプミス(空白、大文字小文字)がある
✅ 解決方法
1. 信頼ポリシーのExternalIdを確認
2. AssumeRole時に指定しているExternalIdと完全一致か確認
3. 前後の空白や改行がないか確認
4. SaaSサービスのダッシュボードで正しいExternalIdを再確認
2. AssumeRole時に指定しているExternalIdと完全一致か確認
3. 前後の空白や改行がないか確認
4. SaaSサービスのダッシュボードで正しいExternalIdを再確認
❌
エラー2: MalformedPolicyDocument
🔍 原因
- 条件キーの大文字小文字が間違っている
- 正しくは
sts:ExternalId(EとIが大文字)
✅ 解決方法
条件キーを
sts:ExternalId に修正(大文字小文字を正確に)
❌
エラー3: 信頼関係が確立されていない
🔍 原因
- 信頼ポリシーのPrincipalにSaaSのアカウントIDが含まれていない
- SaaSアカウントIDが間違っている
✅ 解決方法
信頼ポリシーのPrincipalに正しいSaaSアカウントのARNを追加
(SaaSサービスのドキュメントで正しいアカウントIDを確認)
(SaaSサービスのドキュメントで正しいアカウントIDを確認)
📋 9. CloudTrailでの監査方法
🔍 AssumeRoleイベントの確認方法
1
CloudTrailでイベント検索
CloudTrailコンソールまたはAthenaで、
AssumeRoleイベントを検索します。
# CloudTrail Insights または Athena クエリ
SELECT eventTime, userIdentity.arn, requestParameters
FROM cloudtrail_logs
WHERE eventName = 'AssumeRole'
AND requestParameters LIKE '%ServiceRole%'
ORDER BY eventTime DESC
LIMIT 100;
SELECT eventTime, userIdentity.arn, requestParameters
FROM cloudtrail_logs
WHERE eventName = 'AssumeRole'
AND requestParameters LIKE '%ServiceRole%'
ORDER BY eventTime DESC
LIMIT 100;
2
ExternalIdの確認
イベントの
requestParametersにexternalIdが含まれているか確認。
{
"eventName": "AssumeRole",
"requestParameters": {
"roleArn": "arn:aws:iam::111111:role/ServiceRole",
"roleSessionName": "SaaSSession",
"externalId": "customer-a-x8K9mP2nL5vQ"
}
}
"eventName": "AssumeRole",
"requestParameters": {
"roleArn": "arn:aws:iam::111111:role/ServiceRole",
"roleSessionName": "SaaSSession",
"externalId": "customer-a-x8K9mP2nL5vQ"
}
}
3
不審なアクセスの検出
ExternalIdなしでAssumeRoleが試行されていないか、失敗イベントを確認。
# 失敗したAssumeRole試行を検索
SELECT eventTime, sourceIPAddress, errorCode
FROM cloudtrail_logs
WHERE eventName = 'AssumeRole'
AND errorCode = 'AccessDenied'
ORDER BY eventTime DESC;
SELECT eventTime, sourceIPAddress, errorCode
FROM cloudtrail_logs
WHERE eventName = 'AssumeRole'
AND errorCode = 'AccessDenied'
ORDER BY eventTime DESC;
⭐ 10. ベストプラクティス
💡 ExternalId活用の鉄則
推測困難な文字列を使用
UUID v4形式など、暗号学的に安全な乱数を使用。短い文字列や連番は避ける。
✅ a1b2c3d4-e5f6-7890-abcd-ef1234567890
❌ customer-001, test123
❌ customer-001, test123
顧客ごとに固有の値
SaaSサービスは顧客ごとに異なるExternalIdを発行すべき。共通IDは意味がない。
顧客A: cust-a-xK9mP2nL5vQ
顧客B: cust-b-yZ3qR7tW8uE
顧客B: cust-b-yZ3qR7tW8uE
秘密として厳重管理
ExternalIdはパスワード同様に秘密にする。GitHubにコミットしない、ログに出力しない。
✅ AWS Secrets Manager / Parameter Store (SecureString)
❌ ソースコード直書き、環境変数のみ
❌ ソースコード直書き、環境変数のみ
定期的なローテーション
漏洩リスクを低減するため、定期的にExternalIdを更新。漏洩が疑われる場合は即座に変更。
推奨: 90日〜1年ごとにローテーション
最小権限の原則
ExternalIdに加えて、IAMロールの権限も必要最小限に。読み取り専用で十分な場合が多い。
例: s3:GetObject, cloudwatch:GetMetricData のみ
多層防御の実践
ExternalIdだけでなく、aws:SourceAccount、セッション時間制限も組み合わせる。
+ MaxSessionDuration: 1時間
+ aws:SourceAccount条件
+ aws:SourceAccount条件
✅ 11. 設定チェックリスト
📋 ExternalId導入時の確認項目
🔐
ExternalIdの準備
-
☐SaaSサービスからExternalIdを取得した
-
☐ExternalIdは推測困難な文字列である
-
☐ExternalIdを安全な場所に保管した
📄
IAMロール設定
-
☐信頼ポリシーにConditionでExternalIdを設定した
-
☐PrincipalにSaaSの正しいアカウントIDを設定した
-
☐権限ポリシーは最小権限になっている
-
☐MaxSessionDurationを適切に設定した
🧪
動作確認
-
☐正しいExternalIdでAssumeRoleが成功する
-
☐誤ったExternalIdでAssumeRoleが失敗する
-
☐ExternalIdなしでAssumeRoleが失敗する
📊
監視・監査
-
☐CloudTrailでAssumeRoleイベントが記録されている
-
☐失敗したAssumeRole試行のアラートを設定した
-
☐定期的なアクセスレビューの計画がある
❓ 12. よくある質問
FAQ
ExternalIdは誰が生成するの?
サードパーティサービス(SaaS側)が生成します。
顧客がサービスに登録すると、サービス側が顧客専用のExternalIdを発行し、「このIDをIAMロールに設定してください」と案内します。顧客が自分で作成する場合もありますが、その場合はサービス側にそのIDを伝える必要があります。
重要:SaaSが発行する場合、そのExternalIdは顧客ごとに固有である必要があります。共通のExternalIdを全顧客に使いまわすと、混乱した代理問題を防げません。
顧客がサービスに登録すると、サービス側が顧客専用のExternalIdを発行し、「このIDをIAMロールに設定してください」と案内します。顧客が自分で作成する場合もありますが、その場合はサービス側にそのIDを伝える必要があります。
重要:SaaSが発行する場合、そのExternalIdは顧客ごとに固有である必要があります。共通のExternalIdを全顧客に使いまわすと、混乱した代理問題を防げません。
自社のクロスアカウントアクセスにも必要?
自社内のアカウント間では通常不要です。
ExternalIdは「第三者サービスとの連携」で発生する「混乱した代理」問題を防ぐためのもの。自社で管理するアカウント同士なら、Principal(信頼するAWSアカウント)を正確に指定すれば十分です。
ただし例外:社内でもマルチテナント型の共有サービスを運用している場合は、ExternalIdの使用を検討してください。
ExternalIdは「第三者サービスとの連携」で発生する「混乱した代理」問題を防ぐためのもの。自社で管理するアカウント同士なら、Principal(信頼するAWSアカウント)を正確に指定すれば十分です。
ただし例外:社内でもマルチテナント型の共有サービスを運用している場合は、ExternalIdの使用を検討してください。
ExternalIdを設定し忘れるとどうなる?
2つのケースがあります:
① 信頼ポリシーにExternalId条件あり、AssumeRole時に指定なし
→ アクセス拒否(AccessDenied)。これは安全な状態。
② 信頼ポリシーにExternalId条件なし
→ 脆弱な状態!ARNを知っている誰でもAssumeRole可能。すぐに修正を!
① 信頼ポリシーにExternalId条件あり、AssumeRole時に指定なし
→ アクセス拒否(AccessDenied)。これは安全な状態。
② 信頼ポリシーにExternalId条件なし
→ 脆弱な状態!ARNを知っている誰でもAssumeRole可能。すぐに修正を!
ExternalIdが漏洩したらどうする?
即座にローテーションしてください:
1. SaaSサービスのダッシュボードで新しいExternalIdを発行(または自分で生成)
2. IAMロールの信頼ポリシーを新しいExternalIdに更新
3. SaaSサービスに新しいExternalIdを設定
4. CloudTrailで不審なアクセスがなかったか確認
予防策:ExternalIdをソースコードにハードコードしない。AWS Secrets Managerなどで管理。
1. SaaSサービスのダッシュボードで新しいExternalIdを発行(または自分で生成)
2. IAMロールの信頼ポリシーを新しいExternalIdに更新
3. SaaSサービスに新しいExternalIdを設定
4. CloudTrailで不審なアクセスがなかったか確認
予防策:ExternalIdをソースコードにハードコードしない。AWS Secrets Managerなどで管理。
ExternalIdだけで完全に安全?
ExternalIdは重要な防御層ですが、完璧ではありません。
ExternalIdで防げること:
• 混乱した代理問題(なりすまし)
• ARNを知っているだけの不正アクセス
ExternalIdで防げないこと:
• ExternalId自体の漏洩
• SaaSサービス自体が侵害された場合
• 過剰な権限を持つIAMロールの悪用
多層防御を推奨:ExternalId + 最小権限 + セッション時間制限 + 監視アラート
ExternalIdで防げること:
• 混乱した代理問題(なりすまし)
• ARNを知っているだけの不正アクセス
ExternalIdで防げないこと:
• ExternalId自体の漏洩
• SaaSサービス自体が侵害された場合
• 過剰な権限を持つIAMロールの悪用
多層防御を推奨:ExternalId + 最小権限 + セッション時間制限 + 監視アラート
🎓 まとめ
ExternalIdとは
なりすましを防ぐ
秘密の合言葉
本人確認用コード
秘密の合言葉
本人確認用コード
防げる攻撃
混乱した代理問題
なりすましアクセス
不正なAssumeRole
なりすましアクセス
不正なAssumeRole
使うべき場面
SaaS連携
外部ベンダー
マルチテナント
外部ベンダー
マルチテナント
実装方法
信頼ポリシーに
Condition追加
StringEquals使用
Condition追加
StringEquals使用