クラウド移行を進める現場で、毎回のように頭を悩ませるのが「レガシーなディレクトリサービスからクラウドIAMへの移行」だ。オンプレミスのActive Directory(AD)に慣れ親しんだエンジニアほど、クラウドの柔軟性と複雑な権限モデルのギャップに足元をすくわれる。
「とりあえず移行期間中だから、管理者権限(AdministratorAccessやOwnerロール)を付与しておいて、落ち着いたら権限を絞ろう」
――この甘い判断が、数々のインシデントを生んできた。私がこれまでに関わったインシデント調査でも、レガシー環境からの移行期に放置された「一時的なはずの過剰権限」が攻撃者に踏み台にされ、クラウド環境全体が蹂躙されたケースは数知れない。
今回は、クラウドIAM移行時における「最小権限の原則(Principle of Least Privilege)」の徹底と、そこにある攻撃者の盲点、そして現場で即座に使える具体的な防御・監査の実装手法を叩き込む。
—
なぜクラウド移行時のIAMは「過剰権限」の温床になるのか?
オンプレミスでは、ファイルサーバーのアクセス権やドメインコントローラーのグループポリシー(GPO)など、比較的「境界防御」の思想に基づいたアクセス制御が機能していた。しかし、AWS、Azure、GCPといったパブリッククラウドにシステムを移行した途端、すべてのリソースがAPI経由で操作可能になる。
攻撃者は、この「APIの隙」を狙ってくる。
攻撃者が好む「移行期の盲点」
1. ワイルドカード (*) の常用: 「動かない原因を切り分けるのが面倒だから」という理由で、リソースやアクションに * を指定したIAMポリシーがそのまま本番環境に残る。
2. 長期化する特権アカウント: 移行作業用の一時アカウントが削除されず、MFA(多要素認証)も未設定のまま放置される。
3. 過剰な権限委譲: 開発者がCI/CDパイプラインやコンテナからクラウドリソースを操作する際、必要以上のインフラ変更権限(iam:PassRoleやec2:*など)を持たされている。
もし、Webアプリケーションに脆弱性(例えば、SSRFや不十分なファイルアップロードなど)が存在し、そこにアタッチされたIAMロールが過剰な権限を持っていた場合、攻撃者はクラウド環境全体のコントロール(Lateral Movement:水平移動)を容易に達成してしまう。
—
最小権限をコードと設定で強制する
「気をつけて運用しましょう」という精神論はセキュリティの世界では無価値だ。仕組みで縛る、すなわち「インフラ・アス・コード(IaC)」と「厳格な検証」によって最小権限を強制する。
ここでは、AWSを例にとり、過剰な権限付与を防ぐための具体的なTerraform(IaC)の設定と、動的に権限を検証・制御するPythonの実装例を紹介する。
1. Terraformによる「最小権限」IAMロールの定義
以下は、特定のS3バケットへの特定操作(読み取りのみ)と、最小限のログ出力権限(CloudWatch Logs)のみを許可するセキュアなIAMポリシーのコード例だ。ワイルドカードを徹底的に排除している。
# セキュアなIAMポリシーの定義(最小権限の原則を適用)
resource "aws_iam_policy" "secure_app_policy" {
name = "SecureAppExecutionPolicy"
description = "アプリケーションが必要最小限の操作のみ行えるように制限されたポリシー"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowReadSpecificS3Bucket"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket"
]
Resource = [
"arn:aws:s3:::my-company-secure-app-data",
"arn:aws:s3:::my-company-secure-app-data/*"
]
},
{
Sid = "AllowCloudWatchLogging"
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
# ロググループのARNを特定のものに限定する
Resource = "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/lambda/secure-app-*"
}
]
})
}
# IAMロールの作成とポリシーのアタッチ
resource "aws_iam_role" "app_execution_role" {
name = "AppExecutionRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy_attachment" "attach_secure_policy" {
role = aws_iam_role.app_execution_role.name
policy_arn = aws_iam_policy.secure_app_policy.arn
}
2. 動的監査:過剰権限(*)を検知するPythonスクリプト
移行期において、既存のIAMポリシーに危険なワイルドカードが含まれていないかを定期的にスキャンする監査スクリプトの例だ。Boto3を使用して、AWS環境内のすべてのカスタマー管理ポリシーを走査し、危険な記述があればアラートを上げる。
import boto3
import json
def audit_iam_policies():
"""
AWS環境内のIAMポリシーを走査し、ワイルドカード(*)を用いた
過剰な権限付与(リスクの高いアクション)がないかを監査するスクリプト。
"""
iam_client = boto3.client('iam')
# スコープを狭めるため、カスタマー管理ポリシーのみを取得
policies = iam_client.list_policies(Scope='Local')['Policies']
high_risk_actions = ['*', 'iam:*', 's3:*', 'ec2:*', 'sts:*']
violations = []
print("[*] IAMポリシーの最小権限監査を開始します...")
for policy in policies:
policy_arn = policy['Arn']
policy_name = policy['PolicyName']
default_version_id = policy['DefaultVersionId']
# ポリシーのドキュメント(JSON)を取得
policy_version = iam_client.get_policy_version(
PolicyArn=policy_arn,
VersionId=default_version_id
)
document = policy_version['PolicyVersion']['Document']
# ステートメントを解析
statements = document.get('Statement', [])
if isinstance(statements, dict):
statements = [statements]
for stmt in statements:
if stmt.get('Effect') == 'Allow':
actions = stmt.get('Action', [])
if isinstance(actions, str):
actions = [actions]
resources = stmt.get('Resource', [])
if isinstance(resources, str):
resources = [resources]
# ワイルドカードの検出ロジック
for action in actions:
if action in high_risk_actions or ('*' in action and any(hr in action for hr in ['iam', 's3', 'ec2'])):
if '*' in resources:
violations.append({
'PolicyName': policy_name,
'Arn': policy_arn,
'RiskAction': action,
'RiskResource': resources
})
# 監査結果の出力
if violations:
print(f"[!] 警告: {len(violations)} 件の過剰権限ポリシーを検出しませんでした!")
for v in violations:
print(f" - ポリシー名: {v['PolicyName']}")
print(f" ARN: {v['Arn']}")
print(f" 危険なアクション: {v['RiskAction']}")
print(f" 対象リソース: {v['RiskResource']}\n")
# 実際の運用ではここでSlackやSNSへ通知を飛ばす
else:
print("[+] 監査完了: 致命的なワイルドカード権限は検出されませんでした。")
if __name__ == '__main__':
audit_iam_policies()
—
セキュリティチーフからの実務アドバイス:移行期を乗り切る極意
クラウドIAMの移行において、完璧な状態を初日から目指すのは現実的ではない。だからこそ、以下の「段階的な締め付け(フェーズド・アプローチ)」をチームの共通認識として持ってほしい。
1. アクセスログ(CloudTrail / Access Advisor)の活用: 移行直後は、ユーザーやアプリケーションが実際にどのAPIを叩いているのか分からない。IAMの「Last Accessed Data(最後にアクセスした日時)」機能を使い、本当に使われている権限だけをあぶり出せ。
2. 「とりあえず管理者」の禁止と自動失効: やむを得ず一時的な特権を付与する場合は、AWS IAM Identity CenterやAzure ADのPIM(Privileged Identity Management)を活用し、「最大8時間で権限が自動剥奪される仕組み」を強制すること。
3. CI/CDパイプラインへの静的解析の組み込み: 先ほど紹介したようなIAMポリシーのチェックを、GitHub ActionsやGitLab CIなどのパイプラインに組み込む。checkovやtfsecといったOSSツールを導入し、*が含まれるIaCコードはプルリクエストの段階でマージ不可にする。
セキュリティは「面倒くさい」を排除した瞬間から崩壊する。レガシーからの脱却は、単なるインフラの引越しではなく、「ゼロトラストの思想を組織にインストールする絶好のチャンス」だ。後回しにされた権限管理のツケは、必ず最悪のタイミングで支払わされることになる。今のうちに、手元のコードとポリシーを見直してほしい。
コメント