본문으로 건너뛰기
aws검색
운영, 보안, 비용

S3 403 Forbidden 해결 (버킷 정책, 객체 소유권, KMS, 엔드포인트)

S3 에서 403 AccessDenied 가 날 때 객체가 없어서인지 권한이 없어서인지 구분하는 법, 버킷 ARN 과 객체 ARN 혼동, 퍼블릭 액세스 차단, 객체 소유권, SSE-KMS 키 권한, VPC 엔드포인트 정책, CloudFront OAC, 미리 서명된 URL 만료까지 원인별 해결책을 증상, 원인, 해결 순서로 정리합니다.

S3403버킷 정책KMS트러블슈팅2026-10 기준, 약 7분

증상

$ aws s3 cp s3://lake-prod/raw/2026/10/20/events.json .
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

$ curl -I https://lake-prod.s3.ap-northeast-2.amazonaws.com/img/logo.png
HTTP/1.1 403 Forbidden
x-amz-request-id: 4QF0...

S3 의 403 은 이유가 아주 다양합니다. 또 하나 기억할 점은 s3:ListBucket 권한이 없으면 존재하지 않는 객체도 404 가 아니라 403 으로 응답한다는 것입니다. 객체 키 오타가 403 으로 보일 수 있습니다.

원인

원인대표 상황
신원 정책에 작업이나 리소스가 없음GetObject 를 버킷 ARN 에만 허용
버킷 정책의 명시적 Denyaws:SourceVpce, aws:SecureTransport, aws:PrincipalOrgID 조건
퍼블릭 액세스 차단퍼블릭 읽기 버킷 정책이 무시되거나 거부됨
객체 소유권다른 계정이 올린 객체(ACL 시절 버킷)
SSE-KMS 키 권한kms:Decrypt 없음, 키 정책이 상대 계정을 허용하지 않음
VPC 엔드포인트 정책게이트웨이 엔드포인트 정책이 해당 버킷을 허용하지 않음
CloudFront OAC 설정 불일치버킷 정책의 배포 ARN 이 다름, KMS 키 정책 누락
미리 서명된 URL만료, 서명한 자격 증명의 권한 부족, 리전 불일치
조직 정책SCP, RCP 가 S3 작업이나 외부 접근을 막음

해결

1. 권한 오류인지, 객체가 없는지

aws sts get-caller-identity
aws s3api list-objects-v2 --bucket lake-prod --prefix raw/2026/10/20/ --max-items 5
aws s3api head-object --bucket lake-prod --key raw/2026/10/20/events.json

목록이 보이는데 해당 키가 없으면 키 문제입니다. 목록부터 403 이면 권한 문제입니다. 오류 메시지에 거부한 정책 종류가 나오면 AccessDenied 해결 의 표로 범위를 좁힙니다.

2. 버킷 ARN 과 객체 ARN

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::lake-prod",
      "Condition": { "StringLike": { "s3:prefix": ["raw/*"] } } },
    { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::lake-prod/raw/*" }
  ]
}

ListBucket 은 버킷 ARN, GetObject, PutObject 는 /* 가 붙은 객체 ARN 에 줘야 합니다. 둘을 하나의 문장에 섞어 쓰면 반만 동작합니다.

3. 버킷 정책의 Deny 조건

aws s3api get-bucket-policy --bucket lake-prod --query Policy --output text | python3 -m json.tool
  • aws:SourceVpce 로 특정 엔드포인트만 허용하는 버킷은 콘솔, 로컬 PC 에서 접근하면 403 입니다. 의도된 동작입니다.
  • aws:SecureTransport 가 false 면 거부하는 정책은 HTTP 요청을 막습니다.
  • 정책을 잘못 써서 관리자도 못 들어가면, 계정 루트 사용자로 버킷 정책을 지울 수 있습니다.

4. 객체 소유권과 ACL

2023-04 이후 새 버킷은 객체 소유권이 "버킷 소유자 적용" 이고 ACL 이 꺼져 있습니다. 예전 버킷에서 다른 계정이 올린 객체는 그 계정이 소유자라서 버킷 소유자도 못 읽는 일이 생깁니다. 객체 소유권을 BucketOwnerEnforced 로 바꾸면 모든 객체의 소유자가 버킷 소유자가 되고 ACL 이 무시됩니다.

aws s3api put-bucket-ownership-controls --bucket lake-prod \
  --ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'

5. SSE-KMS 로 암호화된 객체

객체가 고객 관리형 KMS 키로 암호화돼 있으면 읽는 쪽에 kms:Decrypt, 쓰는 쪽에 kms:GenerateDataKey 가 필요합니다. 교차 계정이면 키 정책에 상대 계정의 역할을 넣고, 상대 역할의 IAM 정책에도 키 ARN 을 넣어야 합니다. AWS 관리형 키(aws/s3)로 암호화한 객체는 교차 계정으로 공유할 수 없으므로, 공유할 데이터는 처음부터 고객 관리형 키나 SSE-S3 를 씁니다.

aws s3api head-object --bucket lake-prod --key raw/2026/10/20/events.json \
  --query '[ServerSideEncryption,SSEKMSKeyId]'

6. VPC 엔드포인트 정책

프라이빗 서브넷에서만 403 이 나면 S3 게이트웨이 엔드포인트의 정책을 봅니다. 엔드포인트 정책이 특정 버킷만 허용하면, 다른 버킷(예: 패키지 저장소, 다른 팀 버킷)으로 가는 요청이 막힙니다.

aws ec2 describe-vpc-endpoints --filters Name=service-name,Values=com.amazonaws.ap-northeast-2.s3 \
  --query 'VpcEndpoints[].PolicyDocument' --output text

7. CloudFront 뒤의 S3

  • 버킷 정책의 AWS:SourceArn 조건이 실제 배포 ARN 과 같은지 확인합니다.
  • 객체가 SSE-KMS 라면 키 정책에 cloudfront.amazonaws.com 서비스 주체와 배포 ARN 조건을 더합니다.
  • 루트 경로(/)나 하위 폴더 경로가 403 이면 기본 루트 객체(index.html) 설정, CloudFront Functions 의 URL 재작성 여부를 봅니다. OAC 를 쓰면 없는 키도 403 으로 옵니다.
  • 예시 버킷 정책은 S3 문서에 있습니다.

8. 미리 서명된 URL

  • 만료 시간이 지났거나, 서명에 쓴 임시 자격 증명(역할 세션)이 먼저 만료되면 URL 도 그때 무효가 됩니다.
  • 서명한 주체에게 그 객체의 권한이 있어야 합니다. URL 은 권한을 새로 만들지 않고 서명자의 권한을 빌려 줄 뿐입니다.
  • 서명할 때의 리전과 버킷 리전이 다르면 서명 오류가 납니다. SDK 클라이언트를 버킷 리전(ap-northeast-2)으로 만듭니다.

원인을 빨리 찾는 도구

  • CloudTrail 데이터 이벤트를 켠 버킷이면 403 요청의 errorCode, userIdentity, vpcEndpointId 가 남습니다.
  • S3 서버 액세스 로그에도 요청자와 상태 코드가 남습니다.
  • IAM Access Analyzer 는 버킷 정책의 외부 공개와 문법 문제를 알려 줍니다.

점검표

확인질문
주체get-caller-identity 결과가 예상한 역할인가
작업과 ARN버킷 ARN 과 객체 ARN 을 나눴는가
버킷 정책조건부 Deny 가 이 요청을 막는가
소유권BucketOwnerEnforced 인가
암호화KMS 키 정책과 IAM 정책 둘 다 허용하는가
네트워크VPC 엔드포인트 정책이 이 버킷을 허용하는가

이 문서에서