TT Lab
Get started
Learn Learning paths Courses

Cloud Permission Design

Taking a Policy JSON Apart Line by Line

Continue in TT Lab

In one line

A policy is, in the end, a document that writes down four things — what (Action) on where (Resource), allowed or denied (Effect), under which conditions (Condition).

Why it was needed

A policy can be syntactically valid and still open more widely than intended. If you check only the allow effect and do not read the resource scope and conditions, a policy that passed review becomes a privilege escalation path. So you need to break a policy down by component and read dangerous combinations in a fixed order.

How it works

The minimal form

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadReportsOnly",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::acme-reports",
        "arn:aws:s3:::acme-reports/*"
      ]
    }
  ]
}

Version looks like a date but is the version of the policy language. Use 2012-10-17 as it is. If you change it, features such as variable substitution disappear.

Why Resource has two lines

This is the place where beginners go wrong most often.

They are different resources. If you write only one, you get "the list shows but the file won't open," or the reverse.

The structure of an ARN

arn:aws:s3:::acme-reports/reports/2026/*
 │   │   │  │ │        └ 자원 경로
 │   │   │  │ └ 계정 ID (S3 는 비움)
 │   │   │  └ 리전 (S3 는 전역이라 비움)
 │   │   └ 서비스
 │   └ 파티션 (aws / aws-cn / aws-us-gov)
 └ 고정

The blank fields are not a typo; they mean that service is global.

Wildcards — the danger differs by position

Expression Meaning Risk
"Action": "s3:Get*" All read-type actions Low
"Action": "s3:*" All S3 actions (including delete) High
"Action": "*" All actions of all services Administrator
"Resource": "*" All resources Depends on context

If "Action": "*" and "Resource": "*" are together in one statement, that is administrator permission. It is the combination to look for first in a policy review.

Condition — where it is truly narrowed

It is the most powerful means of narrowing permissions, yet the least used.

"Condition": {
  "IpAddress":     { "aws:SourceIp": ["203.0.113.0/24"] },
  "Bool":          { "aws:SecureTransport": "true" },
  "StringEquals":  { "aws:PrincipalTag/team": "platform" },
  "DateLessThan":  { "aws:CurrentTime": "2026-12-31T00:00:00Z" }
}

Multiple keys within one Condition block are joined by AND, and multiple values for one key are OR. If you mix up this rule, you get a policy that is the exact opposite of what you intended.

NotAction is a trap

{ "Effect": "Allow", "NotAction": "iam:*", "Resource": "*" }

It reads as "allow everything except IAM," but in fact every new service that appears in the future is also allowed automatically. That is because it is a blacklist, not a whitelist. Always write permissions as an allow list.

The order of reading

When reviewing, this order is quick.

  1. Start with the Effect: Deny statements — what is absolutely blocked
  2. Does Action contain a *?
  3. Does Resource contain a *?
  4. Is there a Condition? — if not, that permission works from anywhere
  5. Is there a NotAction / NotResource? — if so, be suspicious

What it looks like in the field

What to look at next

How to grant permissions without handing out long-term keys — roles and temporary credentials.