# AWS IAM 정책 JSON으로 S3 버킷 읽기 권한만 만들고 시뮬레이터로 확인하기

> IAM 정책 편집기의 JSON 탭에 한 버킷의 목록 조회와 객체 읽기만 허용하는 정책을 붙여 만들고, 정책 시뮬레이터의 새 정책 모드에서 읽기는 허용되고 쓰기와 삭제는 거부되는 것을 확인합니다. 다른 버킷이 거부되는지는 시험하지 않았습니다. 확인이 끝나면 정책을 삭제합니다.

- 주소: https://copyandrun.com/blog/aws-iam-user-least-privilege-policy/
- 분야: 클라우드
- 발행일: 2026-10-18
- 태그: AWS, IAM, 최소 권한, S3, 정책 시뮬레이터

개발자나 분석가에게 S3 접근 권한을 줄 때 가장 흔한 실수는 `AmazonS3FullAccess`를 통째로 붙이는 것입니다. 이 글에서는 버킷 하나의 목록 조회와 객체 읽기만 허용하는 정책을 JSON으로 직접 만들고, 정책 시뮬레이터로 무엇이 허용되고 무엇이 거부되는지 확인합니다. 시뮬레이터를 처음 읽는 분은 [IAM 정책 시뮬레이터 글](/blog/aws-iam-policy-simulator-read/)을 먼저 보면 화면이 익숙해집니다.

## 준비물

| 항목 | 내용 |
| --- | --- |
| AWS 계정 | 실습용 IAM 사용자. 정책 생성, 조회, 삭제와 정책 시뮬레이터 사용 권한이 필요합니다(필요한 세부 작업 이름은 이 실습에서 확인하지 않았습니다) |
| 정책 이름 | `cr-demo-s3-one-bucket-read` |
| 대상 버킷 이름 | `cr-demo-reports` (정책 안의 이름일 뿐, 이 실습에서는 버킷을 만들지 않습니다) |
| 예상 소요 시간 | 약 25분 |
| 변경 사항 | 고객 관리형 정책 1개를 만들고 마지막에 삭제합니다. 어떤 사용자나 역할에도 연결하지 않습니다 |

화면의 계정 정보는 가렸습니다.

## 1. 정책 편집기에서 JSON 탭 열기

IAM 콘솔에서 `정책`, `정책 생성`을 차례로 누르고 정책 편집기의 `JSON` 탭을 엽니다. 아래 정책을 편집기에 붙여 넣습니다.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::cr-demo-reports"
    },
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::cr-demo-reports/*"
    }
  ]
}
```

두 문장으로 나눈 이유는 대상이 다르기 때문입니다. `s3:ListBucket`은 버킷 자체에 대한 작업이라 `Resource`에 버킷 ARN을 쓰고, `s3:GetObject`는 버킷 안의 객체에 대한 작업이라 `/*`를 붙인 객체 ARN을 씁니다.

![정책 편집기 JSON 탭에 Version 2012-10-17과 두 개의 Allow 문이 줄 단위로 펼쳐져 있고, 첫 문은 s3:ListBucket과 arn:aws:s3:::cr-demo-reports, 둘째 문은 s3:GetObject와 arn:aws:s3:::cr-demo-reports/*로 표시된 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/01-json-editor.png)

이 실습에서는 `Sid` 없이 한 줄로 입력한 JSON을 `작업` 메뉴의 `가독성을 위한 최적화`로 펼쳤고, 그러자 `Sid` 값이 `VisualEditor0`, `VisualEditor1`로 채워졌습니다. 위 JSON을 그대로 붙여 넣으면 화면의 줄 구성이나 `Sid` 표시는 다를 수 있으며 어느 쪽이든 정책 내용은 같습니다.

편집기 아래로 내리면 검사 결과가 보입니다.

![정책 편집기 아래쪽에 보안 0, 오류 0, 경고 0, 추천 0 으로 표시되고 오른쪽 아래에 다음 단추가 있는 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/02-validation.png)

보안, 오류, 경고, 추천이 모두 0이면 `다음`으로 넘어갑니다. 오류가 있으면 이 칸에 줄 번호와 함께 표시됩니다.

## 2. 이름을 붙이고 요약 확인하기

2단계 `검토 및 생성`에서 정책 이름과 설명을 입력합니다.

| 화면 | 입력칸 | 입력값 |
| --- | --- | --- |
| 정책 세부 정보 | 정책 이름 | `cr-demo-s3-one-bucket-read` |
| 정책 세부 정보 | 설명 | `Read only access to the cr-demo-reports bucket` |

![정책 이름 칸에 cr-demo-s3-one-bucket-read, 설명 칸에 Read only access to the cr-demo-reports bucket이 입력되어 있고 아래 권한 요약에 서비스 S3, 액세스 수준 제한적 나열 읽기, 리소스 Multiple, 요청 조건 None이 표시된 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/03-name.png)

권한 요약에는 `S3`, 액세스 수준 `제한적: 나열, 읽기`, 리소스 `Multiple`이 표시됩니다. 쓰기 수준 작업이 없다는 것을 이 요약에서 한 번 더 볼 수 있습니다. 맨 아래의 `정책 생성`을 누릅니다.

![정책 세부 정보에 유형 고객 관리형, 생성 시간, 권한 탭의 S3 요약이 보이고 ARN 칸은 가려져 있는 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/04-detail.png)

정책이 만들어지면 정책 이름이 위쪽에 표시되고 `연결된 엔터티` 탭에서 연결 현황을 볼 수 있습니다. ARN에는 계정 ID가 들어 있어 이미지에서는 가렸습니다.

## 3. 시뮬레이터에서 허용과 거부 확인하기

정책을 만들었다고 끝내지 않고, 의도한 대로만 허용되는지 시뮬레이터로 확인합니다. IAM 콘솔 왼쪽 메뉴의 `정책 시뮬레이터`를 열면 별도 페이지가 열립니다. 이 실습 시점에는 영문 화면이었고 위쪽에 새 시뮬레이터 안내가 있었습니다. 사용자에게 연결하지 않고도 시험할 수 있도록 위쪽 `Mode`를 `New Policy`로 바꾸고, 왼쪽의 `Create New Policy`를 눌러 같은 JSON을 한 줄로 붙여 넣은 뒤 `Apply`를 눌렀습니다(정책 이름은 화면이 임시로 붙인 값이 들어갑니다). 새 시뮬레이터에서 같은 방식이 되는지는 확인하지 않았습니다.

서비스 목록에서 `Amazon S3`를 고르고, 작업 목록에서 다음 다섯 작업을 선택했습니다.

| 작업 | 시험 목적 | Resource로 지정한 값 |
| --- | --- | --- |
| `GetObject` | 허용되어야 함 | `arn:aws:s3:::cr-demo-reports/report.csv` |
| `ListBucket` | 허용되어야 함 | `arn:aws:s3:::cr-demo-reports` |
| `PutObject` | 거부되어야 함 | `arn:aws:s3:::cr-demo-reports/report.csv` |
| `DeleteObject` | 거부되어야 함 | `arn:aws:s3:::cr-demo-reports/report.csv` |
| `ListAllMyBuckets` | 거부되어야 함 | 지정하지 않음(기본값 `*`) |

작업 행을 펼쳐 `Add Resource`로 ARN을 입력합니다. 시험할 대상을 특정 객체로 정하기 위해 행마다 입력했고, 이 칸을 비워 둔 채 실행하는 시험은 하지 않았습니다. 각 행에 있는 `Include Resource Policy` 상자는 기본값(선택됨) 그대로 두었습니다. 대상 버킷이 실제로 없으므로 버킷 정책은 적용되지 않았을 것이나, 시뮬레이터가 없는 버킷을 어떻게 처리하는지는 확인하지 않았습니다.

![시뮬레이터에서 위쪽의 DeleteObject, GetObject, PutObject 행이 펼쳐져 있고(ListBucket 행은 화면 아래쪽에 있어 이미지에 보이지 않습니다) 각 행의 Resource 칸에 arn:aws:s3:::cr-demo-reports/report.csv가 입력되어 있으며 위쪽에는 5 actions selected, 5 actions not simulated로 표시된 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/05-sim-resources.png)

`Run Simulation`을 누르면 결과가 나옵니다.

![결과 요약이 5 actions selected, 2 actions allowed, 3 actions denied 이고, GetObject와 ListBucket은 allowed 1 matching statements, DeleteObject와 PutObject와 ListAllMyBuckets는 denied Implicitly denied no matching statements 로 표시된 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/06-sim-result.png)

허용과 거부가 갈린 이유가 작업 종류 때문이라는 점을 먼저 짚어 둡니다. 이 시험은 쓰기, 삭제, 버킷 목록 작업이 허용되지 않는 것은 보여 주지만 `cr-demo-reports`가 아닌 다른 버킷이 거부되는지는 보여 주지 않습니다. 그 시험은 하지 않았습니다.

결과는 허용 2건, 거부 3건이었습니다. `GetObject`와 `ListBucket`은 일치하는 문 1개로 허용되었고, `PutObject`, `DeleteObject`, `ListAllMyBuckets`는 일치하는 문이 없어 암묵적 거부로 표시되었습니다. 정책에 Deny 문을 쓰지 않았는데도 거부되는 이유는 IAM이 명시적으로 허용하지 않은 작업을 기본적으로 거부하기 때문입니다.

한 가지 짚어 둡니다. 결과 표의 `Simulation Resource` 열은 이 실습에서 계속 `*`로 보였습니다. 행을 펼쳐 입력한 ARN은 그 열이 아니라 펼친 행 안에서 확인합니다.

## 4. 정리: 정책 삭제

정책 목록에서 이름으로 `cr-demo`를 검색하면 방금 만든 정책 1건만 나옵니다. 그 항목을 선택하고 `삭제`를 누르면 확인 대화상자가 열립니다.

![정책 목록에서 cr-demo-s3-one-bucket-read 한 건이 선택되어 있고 삭제 대화상자가 이 정책 및 모든 버전을 영구적으로 삭제할지 묻고, 확인을 위해 정책 이름을 입력하는 칸이 있는 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/07-delete-dialog.png)

대화상자 제목과 본문의 이름이 만든 정책 하나와 같은지 확인한 뒤 정책 이름을 입력하고 `삭제`를 누릅니다.

![초록색 알림에 정책이 삭제되었습니다라고 표시되고 정책 개수가 1586으로 표시되며 cr-demo 검색 결과는 0건인 것을 확인합니다.](https://copyandrun.com/img/aws-iam-user-least-privilege-policy/08-deleted.png)

삭제 전 7번 이미지의 정책 수는 1587개였고 삭제 뒤에는 1586개로 줄었으며 `cr-demo` 검색 결과는 0건이었습니다.

## 자주 막히는 곳

- **버킷 ARN과 객체 ARN을 하나로 합치기**: S3 작업은 버킷 대상과 객체 대상이 나뉘어 있습니다. 목록 조회는 버킷 ARN, 객체 읽기는 `/*`가 붙은 ARN으로 나누어 씁니다. 하나로 합쳤을 때의 결과는 이 실습에서 시험하지 않았으니 시뮬레이터로 직접 확인해 보세요.
- **시뮬레이터에서 예상과 다르게 거부로 나오는 경우**: 시험하지 않았지만 확인해 볼 만한 점은 두 가지입니다. 작업 행마다 시험할 ARN을 입력했는지, 정책의 `Resource`와 시험 대상 ARN의 버킷 이름과 경로가 같은지입니다.
- **이름이 길어 입력칸 오류**: 정책 이름은 128자까지, 영문자와 숫자, `+=,.@-_`만 쓸 수 있습니다.
- **`s3:GetObject`만 있고 `s3:ListBucket`이 없는 경우**: 객체 이름을 정확히 아는 경우에만 읽을 수 있고 목록은 볼 수 없는 것으로 알려져 있습니다. 이 실습에서 직접 시험한 것은 아닙니다.
- **이 정책만으로 부족할 수 있는 경우**: S3 콘솔에서 버킷 목록을 열려면 `s3:ListAllMyBuckets`가 필요하며 이 정책에는 없습니다. KMS 키로 암호화한 객체는 `kms:Decrypt` 권한이, 버전 관리를 켠 버킷의 특정 버전은 `s3:GetObjectVersion` 권한이 따로 필요합니다. 이 내용은 AWS 문서를 근거로 한 설명이며 이 실습에서 시험하지 않았습니다.

## 이어서 해 볼 것

- 같은 정책에 `Condition`을 더해 특정 IP 대역이나 MFA 사용 여부로 한 번 더 좁혀 보기
- 정책을 IAM 사용자 그룹에 연결한 뒤 시뮬레이터의 `Existing Policies` 모드로 같은 결과가 나오는지 비교하기. 같은 시험에 다른 버킷의 객체를 추가해 거부되는지도 확인해 보시기 바랍니다
- [S3 버킷 보안 기본값 글](/blog/aws-s3-bucket-secure-defaults-console/)의 버킷에 이 정책을 적용해 실제 접근을 시험하기

## 확인한 범위와 확인하지 않은 것

- 확인한 것: JSON 탭에 정책 붙이기, 검사 결과, 정책 생성과 삭제, 시뮬레이터의 새 정책 모드에서 다섯 작업의 허용과 거부 결과.
- 확인하지 않은 것: 정책을 실제 사용자나 역할에 연결해 S3를 호출하는 것. `cr-demo-reports` 버킷은 만들지 않았고 실제 객체를 읽거나 쓰는 시험도 하지 않았습니다. 시뮬레이터 결과는 정책 계산이며 버킷 정책이나 조직 정책 같은 다른 요소는 포함하지 않았습니다.
- 시뮬레이터 화면은 영문 구버전이었고 상단에 새 시뮬레이터 안내가 표시됩니다. 시점에 따라 화면이 달라질 수 있습니다.
