IAM ポリシーの基本構造:Effect・Action・Resource・Condition の違いを完全解説

IAM ポリシーの JSON を初めて読んだとき、EffectActionResourceCondition の四つの要素が何を制御しているのか、それぞれの境界がどこにあるのかが掴みにくい。本記事では、IAM ポリシーの基本構造を実際の運用経験をもとに解説し、よくある誤解と正しいモデルを整理する。

TL;DR:IAM ポリシー基本構造の早見表

要素役割必須典型的な値の例
Effect許可か拒否かを決定するAllow / Deny
Action対象の API アクションを指定するs3:GetObject
Resourceアクションを適用するリソースを限定するarn:aws:s3:::my-bucket/*
Condition追加の条件が満たされた場合のみ Statement を有効化するaws:SourceIp など

IAM ポリシーの仕組み:評価モデルを理解する

IAM ポリシーは、AWS がリクエストを受け取ったときに評価される一連のルールセットだ。ポリシーは一つ以上の Statement で構成され、各 Statement が「誰が・何を・どのリソースに・どんな条件で・許可または拒否するか」を定義する。AWS はリクエストを評価する際、すべての適用可能なポリシーを収集し、以下の優先順位で判定する。

graph TD A["リクエスト受信"] --> B{"明示的 Deny (いずれかのポリシー)"} B -- "あり" --> C["拒否 (Explicit Deny)"] B -- "なし" --> D{"明示的 Allow (いずれかのポリシー)"} D -- "あり" --> E["許可"] D -- "なし" --> F["拒否 (Implicit Deny)"]
  1. デフォルト拒否:明示的な Allow がない限り、すべてのリクエストは暗黙的に拒否される。
  2. 明示的 Deny の優先:どのポリシーにも Effect: Deny の Statement があれば、Allow があっても必ず拒否される。SCP(Service Control Policy)も同様のロジックで動作する。
  3. 明示的 Allow:Deny がなく、Allow が存在する場合のみアクセスが許可される。
「Explicit Deny は消せない壁だ。Allow をいくら積み上げても、Deny が一枚あれば全部無効になる。バケットポリシーと IAM ポリシーが両方存在する場合、どちらかに Deny があれば終わり。」

Effect:Allow か Deny か、それだけだが重要

IAM ポリシーの基本構造において、Effect は Statement の結論を決める要素だ。値は AllowDeny の二択のみで、大文字小文字を区別する。

{
  "Effect": "Allow"
}

運用上で重要なのは、Explicit Deny は Allow より常に優先されるという点だ。同一プリンシパルに対して複数のポリシーが適用されている場合(インラインポリシー、マネージドポリシー、バケットポリシー、SCP など)、どれか一つでも Effect: Deny の Statement がマッチすれば、そのリクエストは拒否される。

Deny を使うべき典型的なシナリオは、特定の IP アドレス範囲以外からの S3 アクセスを全面的にブロックしたい場合や、特定のリージョン外へのリソース作成を SCP で制限したい場合だ。

Action:どの API を許可・拒否するか

Action は、Statement が対象とする AWS API アクションを指定する。形式は サービスプレフィックス:アクション名 で、ワイルドカード(*)も使用できる。

{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject",
    "s3:DeleteObject"
  ]
}

"Action": "*" はすべての AWS サービスのすべての API を許可する。管理者ロールでも、これを使うのは最小権限の原則に反する。本番環境では必ず必要なアクションを列挙すること。

また、Action の代わりに NotAction を使うことで「指定したアクション以外すべて」という逆引き指定も可能だが、意図しない権限昇格につながりやすいため慎重に使う必要がある。

Resource:どのリソースに適用するか

Resource は、Action を適用するリソースを ARN で指定する。これが IAM ポリシーの基本構造の中で最も誤解されやすい要素だ。

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket/*"
}

S3 の場合、バケット操作(s3:ListBucket など)とオブジェクト操作(s3:GetObject など)では必要な ARN が異なる。s3:ListBucket はバケット自体(arn:aws:s3:::my-bucket)に対して指定し、s3:GetObject はオブジェクト(arn:aws:s3:::my-bucket/*)に対して指定する。この区別を間違えると、CLI でオブジェクトを取得しようとしたときに AccessDenied が返る。

一部の Read/List/Describe 系アクションはリソースレベルの権限をサポートしておらず、"Resource": "*" が必須になる。Service Authorization Reference で各アクションのリソースタイプサポートを必ず確認すること。

graph LR A["s3:ListBucket"] --> B["Resource: バケット ARN arn:aws:s3:::my-bucket"] C["s3:GetObject"] --> D["Resource: オブジェクト ARN arn:aws:s3:::my-bucket/*"] E["s3:ListAllMyBuckets"] --> F["Resource: * (リソースレベル権限非対応)"]
  1. バケットレベルの操作(ListBucket など)は、バケット ARN そのものを Resource に指定する。
  2. オブジェクトレベルの操作(GetObject・PutObject など)は、/* サフィックス付きの ARN を指定する。
  3. リソースレベルの権限をサポートしないアクションは * を使う。

Condition:条件付きアクセス制御の仕組み

Condition は、Statement を有効化するための追加条件を定義する。Condition が存在する場合、その条件が満たされたときのみ Statement が評価される。条件が満たされない場合、その Statement はスキップされる(Allow の場合は暗黙的拒否に戻る)。

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket/*",
  "Condition": {
    "IpAddress": {
      "aws:SourceIp": [
        "203.0.113.0/24",
        "198.51.100.0/24"
      ]
    }
  }
}

Condition の構造は 条件演算子 → 条件キー → 値 の三層になっている。上記の例では、IpAddress が演算子、aws:SourceIp がグローバル条件キー、IPアドレスのリストが値だ。

よく使われる条件キーのカテゴリを以下に示す。

カテゴリ条件キーの例用途
グローバル条件キーaws:SourceIp送信元 IP によるアクセス制限
グローバル条件キーaws:RequestedRegion特定リージョンへの操作を制限
グローバル条件キーaws:MultiFactorAuthPresentMFA 認証済みリクエストのみ許可
サービス固有s3:prefixS3 プレフィックスによるアクセス制限
サービス固有iam:PassedToServiceロールの渡し先サービスを制限

Condition の評価で注意が必要なのは、条件キーが存在しない場合の挙動だ。条件キーがリクエストコンテキストに含まれていない場合、条件演算子によって評価結果が変わる。StringEquals などの通常の演算子は条件キーが存在しない場合に false を返すが、...IfExists サフィックス付きの演算子(例:StringEqualsIfExists)はキーが存在しない場合に true を返す。この違いを理解していないと、意図しないアクセス拒否やアクセス許可が発生する。

完全な Statement の例:四要素を組み合わせる

実際のポリシーでは、複数の Statement を組み合わせて必要な権限セットを表現する。以下は、特定の S3 バケットへの読み取りアクセスを MFA 認証済みリクエストに限定するポリシーの例だ。

🔽 完全な IAM ポリシー JSON(クリックして展開)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::my-secure-bucket",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    },
    {
      "Sid": "AllowGetObject",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-secure-bucket/*",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    },
    {
      "Sid": "DenyNonMFAAccess",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-secure-bucket",
        "arn:aws:s3:::my-secure-bucket/*"
      ],
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

三番目の Statement で BoolIfExists を使っているのは、MFA コンテキストが存在しない場合(例:アクセスキーによる直接 API 呼び出し)も確実に拒否するためだ。Bool だけでは、コンテキストキーが存在しない場合に条件が false になり、Deny が適用されない。

よくある誤診:AccessDenied の原因を間違える

本番環境で頻繁に遭遇するパターンを一つ挙げる。

症状: IAM ポリシーに s3:GetObject の Allow を追加したのに、AccessDenied が解消されない。

誤診: ポリシーの反映に時間がかかっているのだろう、と待ち続ける。または Resource の ARN が間違っていると思い込んで修正を繰り返す。

実際の原因: S3 バケットポリシーに明示的 Deny が設定されていた。IAM ポリシーで Allow を追加しても、バケットポリシーの Explicit Deny が優先されるため、アクセスは永遠に拒否される。IAM ポリシーとリソースベースポリシー(バケットポリシー)の両方を確認しないと、この問題は解決しない。

修正手順:

# バケットポリシーを確認して Deny Statement がないかチェックする
aws s3api get-bucket-policy \
  --bucket my-secure-bucket \
  --query Policy \
  --output text
# IAM ポリシーのシミュレーションで評価結果を確認する
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/example-user \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::my-secure-bucket/example-key.txt

simulate-principal-policy の出力に implicitDenyexplicitDeny が含まれる。explicitDeny が返った場合は、どのポリシーが Deny を発行しているかを MatchedStatements フィールドで確認できる。

IAM ポリシー構造の検証:実用的な CLI コマンド

ポリシーを書いたら、デプロイ前に構文と意図を検証する習慣をつけること。IAM Policy Validator は構文エラーと一部のセマンティックエラーを検出できる。

# ポリシードキュメントの構文を検証する
aws accessanalyzer validate-policy \
  --policy-document file://my-policy.json \
  --policy-type IDENTITY_POLICY

--policy-type には IDENTITY_POLICY(IAM ポリシー)または RESOURCE_POLICY(リソースベースポリシー)を指定する。このコマンドは IAM Access Analyzer を使用するため、対象リージョンで Access Analyzer が有効になっている必要がある。

# 既存のマネージドポリシーの内容を確認する
aws iam get-policy-version \
  --policy-arn arn:aws:iam::123456789012:policy/MyPolicy \
  --version-id v1 \
  --query 'PolicyVersion.Document' \
  --output json

IAM ポリシーの基本構造:まとめと次のステップ

IAM ポリシーの基本構造を理解するうえで核心となるのは、Effect・Action・Resource の三要素が Statement の骨格を形成し、Condition がその適用条件を絞り込むという関係性だ。Explicit Deny は常に Allow より優先され、バケットポリシーや SCP など複数のポリシーレイヤーが存在する環境では、どのレイヤーで拒否が発生しているかを特定することが AccessDenied のトラブルシューティングの出発点になる。

  • IAM Policy Simulator を使って、実際のリクエストコンテキストでポリシーを検証する
  • AWS Service Authorization Reference で各アクションのリソースレベル権限サポートを確認する
  • IAM Access Analyzer を有効化して、外部エンティティからのアクセスを継続的に監視する

公式ドキュメント:IAM JSON ポリシー要素リファレンス

用語集

用語説明
StatementIAM ポリシーを構成する個々のルールブロック。Effect・Action・Resource・Condition を含む。
Explicit Denyポリシー内で明示的に指定された拒否。Allow より常に優先される。
ARN(Amazon Resource Name)AWS リソースを一意に識別する識別子。形式は arn:aws:<service>:<region>:<account-id>:<resource>
条件演算子Condition ブロックで使用する比較演算子。StringEqualsIpAddressBool など。
最小権限の原則必要最小限の権限のみを付与するセキュリティ設計原則。IAM 設計の基本。

コメント

このブログの人気の投稿

EC2 SSH接続タイムアウトの原因と修正方法 — セキュリティグループのインバウンドルール完全ガイド

S3パブリックアクセス拒否の原因と解決策:バケットレベルの「Block Public Access」が優先される仕組み

EC2インスタンスIDをメタデータから取得する方法 — IMDSv2が安全な理由