S3 Access Denied: Por que 'Bloquear Acesso Público' impede seu objeto mesmo após torná-lo público

Você fez upload de uma imagem no S3, marcou o objeto como público, copiou a URL e abriu no navegador — e recebeu um 403 Access Denied. A configuração de ACL do objeto está correta, mas o acesso público continua bloqueado. O culpado quase sempre é o Block Public Access no nível do bucket, uma camada de controle que opera acima das ACLs e políticas de objeto e que muita gente ignora até levar o primeiro 403 em produção.

TL;DR — S3 Access Denied com objeto público

CamadaO que controlaO que verificar
Block Public Access (bucket)Bloqueia ACLs e políticas públicas no bucket inteiroaws s3api get-public-access-block --bucket NOME
ACL do objetoPermissão pública no objeto individualaws s3api get-object-acl --bucket NOME --key CHAVE
Bucket PolicyPermissões baseadas em política no bucketaws s3api get-bucket-policy --bucket NOME
Object OwnershipControla se ACLs são respeitadas ou ignoradasaws s3api get-bucket-ownership-controls --bucket NOME

Como o S3 Access Denied acontece — o modelo de autorização em camadas

O S3 não toma decisões de acesso em uma única verificação. Cada requisição passa por múltiplas camadas de avaliação em sequência. Se qualquer camada retornar um bloqueio explícito, a requisição é negada — independentemente do que as camadas subsequentes permitiriam. Entender essa ordem é o que separa um diagnóstico rápido de horas perdidas verificando a ACL errada.

graph TD REQ["Requisição Anônima
GET objeto"] BPA{"Block Public Access
ativo?"} OWN{"Object Ownership
BucketOwnerEnforced?"} BP{"Bucket Policy
tem Deny explícito?"} ACL{"ACL do objeto
tem public-read?"} DENY["403 Access Denied"] ALLOW["200 OK"] REQ --> BPA BPA -- "Sim (qualquer flag)" --> DENY BPA -- "Não" --> OWN OWN -- "Sim (ACLs ignoradas)" --> BP OWN -- "Não" --> ACL ACL -- "Não" --> DENY ACL -- "Sim" --> BP BP -- "Deny explícito" --> DENY BP -- "Allow ou ausente" --> ALLOW
  1. Block Public Access é avaliado primeiro, antes de qualquer ACL ou política. Se qualquer uma das quatro flags estiver ativa, requisições anônimas são bloqueadas imediatamente.
  2. Object Ownership determina se ACLs de objeto têm efeito. Com o modo BucketOwnerEnforced, todas as ACLs são desativadas — mesmo que você tenha definido public-read no objeto.
  3. Bucket Policy pode conceder ou negar acesso público explicitamente via s3:GetObject para o principal *.
  4. ACL do objeto só é avaliada se Block Public Access permitir e Object Ownership não estiver em modo BucketOwnerEnforced.
Pense no Block Public Access como um porteiro na entrada do prédio. Não importa se o inquilino (objeto) deixou a porta do apartamento aberta — se o porteiro não deixar entrar, ninguém chega lá.

Diagnóstico passo a passo do S3 Access Denied

Siga os passos na ordem. Cada passo cobre o que o anterior não consegue detectar.

Passo 1 — Verificar Block Public Access no bucket

Esta é a causa mais comum e deve ser a primeira verificação. As quatro flags do Block Public Access operam de forma independente, e basta uma delas estar ativa para bloquear o acesso público.

aws s3api get-public-access-block \
  --bucket NOME-DO-BUCKET \
  --region us-east-1

Saída esperada quando o acesso público está bloqueado:

{
    "PublicAccessBlockConfiguration": {
        "BlockPublicAcls": true,
        "IgnorePublicAcls": true,
        "BlockPublicPolicy": true,
        "RestrictPublicBuckets": true
    }
}

Se qualquer flag estiver true e você precisa de acesso público, desative as flags relevantes:

aws s3api put-public-access-block \
  --bucket NOME-DO-BUCKET \
  --region us-east-1 \
  --public-access-block-configuration \
    "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

Atenção: Desativar o Block Public Access expõe o bucket a requisições anônimas. Avalie se o caso de uso realmente exige acesso público irrestrito ou se uma URL pré-assinada atende melhor.

Passo 2 — Verificar Object Ownership

Mesmo com Block Public Access desativado, se o bucket estiver no modo BucketOwnerEnforced, as ACLs de objeto são completamente ignoradas. Esse modo passou a ser o padrão para novos buckets criados após abril de 2023, o que quebra silenciosamente fluxos que dependiam de public-read via ACL.

aws s3api get-bucket-ownership-controls \
  --bucket NOME-DO-BUCKET \
  --region us-east-1

Se a resposta for BucketOwnerEnforced, ACLs não têm efeito. Nesse caso, use uma bucket policy para conceder acesso público em vez de ACL de objeto.

Passo 3 — Verificar a ACL do objeto

Se Object Ownership não estiver em modo BucketOwnerEnforced, verifique se a ACL do objeto realmente concede leitura pública. Definir o objeto como público via console nem sempre persiste se o Block Public Access estava ativo no momento do upload.

aws s3api get-object-acl \
  --bucket NOME-DO-BUCKET \
  --key caminho/para/objeto.jpg \
  --region us-east-1

Procure por um grant com URI: http://acs.amazonaws.com/groups/global/AllUsers e permissão READ. Se não estiver presente, aplique a ACL:

aws s3api put-object-acl \
  --bucket NOME-DO-BUCKET \
  --key caminho/para/objeto.jpg \
  --acl public-read \
  --region us-east-1

Passo 4 — Verificar a Bucket Policy

Uma política de bucket com Deny explícito sobrepõe qualquer ACL ou permissão concedida. Verifique se existe alguma instrução negando s3:GetObject para o principal * ou para o objeto em questão.

aws s3api get-bucket-policy \
  --bucket NOME-DO-BUCKET \
  --region us-east-1 \
  --query Policy \
  --output text | python3 -m json.tool

Se quiser conceder acesso público via política (abordagem recomendada quando ACLs estão desativadas):

🔽 Clique para expandir — Bucket Policy para acesso público de leitura
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::NOME-DO-BUCKET/*"
    }
  ]
}

Aplique com:

aws s3api put-bucket-policy \
  --bucket NOME-DO-BUCKET \
  --region us-east-1 \
  --policy file://policy.json

Passo 5 — Confirmar acesso público com requisição anônima

Após ajustar as configurações, valide com uma requisição genuinamente anônima — sem credenciais AWS. O --no-sign-request simula exatamente o que um navegador faria ao acessar a URL pública.

aws s3api get-object \
  --bucket NOME-DO-BUCKET \
  --key caminho/para/objeto.jpg \
  --no-sign-request \
  /tmp/teste-objeto.jpg

Alternativamente, com curl direto na URL pública:

curl -I "https://NOME-DO-BUCKET.s3.us-east-1.amazonaws.com/caminho/para/objeto.jpg"

Um HTTP 200 confirma que o acesso público está funcionando. Um 403 persistente indica que ainda há uma camada bloqueando — volte ao Passo 1 e revise cada flag individualmente.

graph LR P1["Passo 1
Block Public Access"] --> P2["Passo 2
Object Ownership"] P2 --> P3["Passo 3
ACL do objeto"] P3 --> P4["Passo 4
Bucket Policy"] P4 --> P5["Passo 5
Teste anônimo"] P5 --> OK["200 OK"] P1 -- "flags ativas" --> FIX1["put-public-access-block"] P2 -- "BucketOwnerEnforced" --> FIX2["usar Bucket Policy"] P3 -- "sem public-read" --> FIX3["put-object-acl"] P4 -- "Deny explícito" --> FIX4["revisar policy"] P5 -- "403 persiste" --> P1

Diagnóstico de experiência — o caso do BlockPublicPolicy silencioso

Um cenário recorrente: a equipe desativa BlockPublicAcls e IgnorePublicAcls, aplica a ACL public-read no objeto, e o acesso ainda falha com 403. Horas depois, alguém percebe que RestrictPublicBuckets ainda está true.

O diagnóstico errado inicial é focar na ACL do objeto — afinal, foi o que foi configurado. A ACL está correta. O problema é que RestrictPublicBuckets bloqueia o acesso de qualquer principal anônimo ao bucket, independentemente de ACLs ou políticas que concedam acesso. Ele opera no nível da requisição, não no nível da política.

Outro caso: a equipe tenta aplicar uma bucket policy pública, mas a chamada put-bucket-policy retorna AccessDenied imediatamente — sem aplicar a política. Isso acontece porque BlockPublicPolicy está ativo. Quando essa flag está true, o S3 rejeita qualquer tentativa de aplicar uma política que conceda acesso público, retornando erro na própria chamada de API. Não é um bloqueio silencioso — é uma falha explícita na operação de escrita da política.

sequenceDiagram participant Eng as Engenheiro participant S3 as S3 API participant BPA as Block Public Access Eng->>S3: put-bucket-policy (concede acesso público) S3->>BPA: verifica BlockPublicPolicy BPA-->>S3: BlockPublicPolicy=true S3-->>Eng: AccessDenied (política rejeitada) Note over Eng,S3: A política nunca é aplicada Eng->>S3: put-public-access-block (desativa flags) S3-->>Eng: 200 OK Eng->>S3: put-bucket-policy (nova tentativa) S3-->>Eng: 200 OK (política aplicada) Eng->>S3: GET objeto (sem credenciais) S3-->>Eng: 200 OK

A lição: as quatro flags do Block Public Access são independentes. Desativar uma não desativa as outras. Sempre verifique o estado completo da configuração com get-public-access-block antes de investigar ACLs.

Verificar Block Public Access no nível da conta

Existe uma camada adicional que opera acima do bucket: o Block Public Access no nível da conta AWS. Se estiver ativo, ele bloqueia o acesso público em todos os buckets da conta, independentemente das configurações individuais de cada bucket.

aws s3control get-public-access-block \
  --account-id 123456789012 \
  --region us-east-1

Se precisar desativar no nível da conta (faça isso com cautela — afeta todos os buckets):

aws s3control put-public-access-block \
  --account-id 123456789012 \
  --region us-east-1 \
  --public-access-block-configuration \
    "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

Política IAM para gerenciar Block Public Access

Para permitir que uma role ou usuário gerencie as configurações de Block Public Access tanto no nível de bucket quanto de conta:

🔽 Clique para expandir — Política IAM para gerenciar Block Public Access
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "GerenciarBlockPublicAccessBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketPublicAccessBlock",
        "s3:PutBucketPublicAccessBlock"
      ],
      "Resource": "arn:aws:s3:::NOME-DO-BUCKET"
    },
    {
      "Sid": "GerenciarBlockPublicAccessConta",
      "Effect": "Allow",
      "Action": [
        "s3control:GetPublicAccessBlock",
        "s3control:PutPublicAccessBlock"
      ],
      "Resource": "*"
    }
  ]
}

Alternativas ao acesso público irrestrito

Na maioria dos casos de uso — compartilhar uma imagem, permitir download temporário, servir assets para uma aplicação — o acesso público irrestrito não é necessário. URLs pré-assinadas oferecem acesso temporário e auditável sem expor o bucket publicamente:

aws s3 presign s3://NOME-DO-BUCKET/caminho/para/objeto.jpg \
  --expires-in 3600 \
  --region us-east-1

O comando retorna uma URL com credenciais temporárias embutidas, válida pelo tempo especificado em segundos. O bucket permanece privado.

Próximos passos e referências para S3 Access Denied

Se o 403 persistir após verificar todas as camadas acima, verifique também: SCPs (Service Control Policies) aplicadas à conta via AWS Organizations, VPC Endpoint Policies se o acesso for originado de dentro de uma VPC, e logs do S3 Server Access Logging ou AWS CloudTrail para identificar exatamente qual camada está retornando o bloqueio.

Glossário

TermoDefinição
Block Public AccessConjunto de quatro flags no S3 que bloqueiam acesso público no nível de bucket ou conta, operando acima de ACLs e políticas.
ACL (Access Control List)Mecanismo legado do S3 para conceder permissões em buckets e objetos individuais, incluindo a permissão public-read.
Object OwnershipConfiguração de bucket que controla se ACLs de objeto têm efeito. No modo BucketOwnerEnforced, ACLs são desativadas.
Bucket PolicyPolítica baseada em recursos aplicada ao bucket, avaliada após Block Public Access e Object Ownership.
URL Pré-assinadaURL temporária com credenciais embutidas que permite acesso a um objeto S3 privado por um período determinado.

Comentários

Postagens mais visitadas deste blog

EC2 SSH Connection Timeout: Quais Regras do Security Group Verificar

EC2 Sem Acesso à Internet em VPC Customizada: Como Configurar Internet Gateway e Route Table