← 목록

S3 Presigned URL 관련 자원 보안 점검: IAM, 버킷 정책, 퍼블릭 액세스 감사

설계대로 되어 있는지 8개 버킷을 전수 점검한 기록. 점검 항목을 정한 기준과, 개발 버킷 하나에서 나온 문제를 어떻게 평가했는지

2026-04-06AWSS3SecurityIAM

들어가며

S3 접근 제어 설계를 정리한 지 시간이 좀 지났다. 그 사이에 버킷이 늘었고, 사람이 바뀌었고, 급하게 열어준 것들이 생겼다.

설계 문서는 "이렇게 하기로 했다"를 적은 것이지 "지금 이렇다"를 보장하지 않는다. 그래서 실제 상태를 한 번 훑기로 했다. 이런 점검은 문서를 다시 읽어서는 아무것도 나오지 않고, 리소스를 하나씩 열어봐야 나온다.

무엇을 볼지 먼저 정했다

항목을 즉흥적으로 고르면 점검할 때마다 결과가 달라진다. 그래서 각 항목이 어떤 사고를 막는지를 기준으로 목록을 만들었다.

점검 항목막으려는 상황
퍼블릭 액세스 차단실수로 붙인 정책 하나로 버킷이 공개되는 것
서버측 암호화저장 매체 수준에서 데이터가 노출되는 것
버킷 정책의 Principal과 Action 범위필요 이상의 권한이 남아 있는 것
HTTPS 강제전송 구간에서 데이터와 서명이 노출되는 것
Access Key 로테이션유출된 키가 계속 유효한 것
액세스 로깅사고 이후에 무슨 일이 있었는지 모르는 것

점검 결과 한눈에 보기

총 8개 버킷을 점검했다.

점검 항목별 결과

항목양호주의
퍼블릭 액세스 차단7개1개 (전체 미차단)
서버측 암호화 (AES256)8개 전체없음
버전 관리1개대부분 미설정
HTTPS 강제 (DenyInsecureTransport)3개일부 누락
액세스 로깅0개전체 미설정

설계의 핵심이었던 접근 통제(퍼블릭 차단, 암호화)는 대체로 지켜지고 있었다. 반면 사고가 난 뒤에 필요한 항목(액세스 로깅, 버전 관리)은 거의 비어 있었다. 급할 때 먼저 챙기는 것과 나중에 필요한 것이 이렇게 나뉜다는 게 표에서 그대로 보였다.


Presigned URL 전용 IAM User 점검

Presigned URL 서명 전용 IAM User를 prod와 dev로 분리해 운영 중이다.

권한 설계

{
  "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
  "Resource": [
    "arn:aws:s3:::[bucket-name]/[prefix-1]/*",
    "arn:aws:s3:::[bucket-name]/[prefix-2]/*",
    "arn:aws:s3:::[bucket-name]/[prefix-3]/*"
  ]
}

특정 prefix에만 접근을 제한해 최소 권한 원칙을 적용했다. Presigned URL은 서명한 주체의 권한 범위 안에서만 동작하기 때문에, 이 유저의 권한을 좁히는 것이 곧 URL로 할 수 있는 일을 좁히는 것이 된다. 서명 유저를 따로 둔 이유가 여기에 있다.

Access Key 상태

환경키 경과일마지막 사용상태
prod42일21일 전실제 사용 여부 확인 필요
dev42일3일 전정상 사용 중

90일 로테이션 권장 기준 안에 있지만, prod 키가 21일간 쓰이지 않은 점이 눈에 걸렸다. 쓰이지 않는 키가 더 위험하다. 아무도 사용하지 않으면 누가 언제 가져다 써도 이상한 점을 눈치채기 어렵기 때문이다. 경과일만 보지 말고 마지막 사용일을 같이 봐야 하는 이유다.

확인 결과에 따라 셋 중 하나를 택한다. 실제로 쓰이는데 주기가 긴 것이라면 그대로 두고, 대체된 것이라면 삭제하고, 용도를 아무도 모르면 비활성화 후 반응을 본다.


버킷 정책 분석

양호한 패턴

운영과 개발의 주요 버킷은 다음 패턴을 따르고 있었다.

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:*",
  "Resource": ["arn:aws:s3:::[bucket-name]", "arn:aws:s3:::[bucket-name]/*"],
  "Condition": {
    "Bool": { "aws:SecureTransport": "false" }
  }
}

Deny 문을 쓴 게 핵심이다. IAM 정책에서 Allow를 아무리 좁혀도, 버킷 정책에 Allow가 넓게 열려 있으면 다른 경로로 들어올 수 있다. Deny는 어떤 Allow보다 우선하므로, "이것만은 절대 안 된다"를 버킷 쪽에 박아두는 방식이 확실하다.

위험 발견: 퍼블릭 액세스 미차단 버킷

개발 버킷 하나에서 문제가 나왔다.

개발 버킷이라 괜찮지 않느냐는 이야기가 나올 수 있어서, 왜 급한 항목으로 분류했는지 적어둔다.

즉 지금 이 버킷이 얼마나 중요한지가 아니라, 이 상태가 유지될 때 무엇이 가능해지는지로 판단했다. 같은 기준으로 퍼블릭 액세스 차단 가이드에서도 "차단 비활성 + 정책 없음" 상태를 주의로 분류했다.


발견된 문제점과 권장 조치

긴급 (보안 위험)

문제조치
퍼블릭 액세스 차단 미설정4개 항목 모두 활성화
버킷 정책에 s3:* + Principal:* 허용최소 권한으로 축소 (Get/Put/List만)
HTTPS 미강제aws:SecureTransport 조건 추가
Access Key 90일 로테이션 초과 (온프레미스 서버용, 약 160일)즉시 키 로테이션

주의 (운영 개선)

문제조치
ECS Task Role에 버킷 전체 권한presign 전용 유저로 분리했으므로 Task Role의 S3 권한 축소 검토
fullaccess 유저에 s3:* 권한필요한 Action만 남기도록 축소
버전 관리 미설정 (운영 버킷)데이터 보호를 위해 활성화 검토
DenyInsecureTransport 누락버킷 정책에 HTTP 차단 Deny 문 추가
액세스 로깅 미설정 (전체)감사 추적을 위해 활성화 검토

버전 관리는 전부 켜는 게 답이 아니라서, 판단 기준을 따로 정리했다(S3 버킷 버전 관리 적용 가이드). 액세스 로깅도 마찬가지로 대상과 보존 기간을 정하고 켜야 한다.

정상 항목

정상 항목을 굳이 적는 이유는 다음 점검 때문이다. 무엇이 확인됐는지 남겨두지 않으면 매번 전부를 처음부터 다시 본다.


정리


배운 점

설계는 시간이 지나면 실제와 어긋난다. 대부분은 원칙대로였는데 버킷 하나가 예외였고, 그건 문서를 다시 읽어서는 찾을 수 없는 종류였다. 설계 문서와 실제 상태를 맞춰보는 작업은 따로 필요하다.

쓰이지 않는 자격증명이 더 위험하다. 경과일만 보던 기준에 마지막 사용일을 추가한 게 이번 점검의 수확이다. 아무도 쓰지 않는 키는 누가 가져다 써도 티가 나지 않는다.

위험도는 지금의 중요도가 아니라 가능한 일로 판단한다. 개발 버킷이라는 이유로 넘기지 않은 건, 그 상태에서 무엇이 가능해지는지를 봤기 때문이다. 같은 기준을 퍼블릭 액세스 차단 가이드에서도 썼다.

이 작업은 반복되어야 하므로 사람이 하면 안 된다. 전수 점검은 한 번 하면 그 시점의 사진일 뿐이다. 그래서 항목을 규칙으로 옮겼고, 그 결과가 AWS Config 기반 인프라 규정 준수 모니터링이다. 점검은 일회성 작업이 아니라 상태를 유지하는 일에 가깝다.


이어 읽기