S3 Acceso Denegado: Por Qué 'Block Public Access' Anula los Permisos del Objeto

Subiste una imagen a S3, marcaste el objeto como público, copiaste la URL y... Access Denied. Es uno de los errores más frustrantes para alguien que empieza con S3, y también para veteranos que configuran un bucket nuevo sin revisar los ajustes de cuenta. El problema casi siempre no está en el objeto — está en la capa de bloqueo que AWS puso por encima de todos los permisos.

TL;DR — Resumen Rápido

CapaQué controlaPrioridad
Block Public Access (cuenta)Bloquea acceso público en todos los buckets de la cuentaMás alta
Block Public Access (bucket)Bloquea acceso público en ese bucket específicoAlta
Bucket PolicyPermite o deniega acceso a nivel de bucketMedia
ACL del objetoPermiso individual por objeto (legacy)Baja

Si Block Public Access está activo en cualquiera de las dos capas superiores, los permisos del objeto y la bucket policy son irrelevantes — el acceso público queda bloqueado sin importar qué más hayas configurado.

Cómo Funciona el Control de Acceso Público en S3

S3 tiene un modelo de permisos en capas. Antes de que AWS evalúe si un objeto tiene ACL pública o si la bucket policy permite s3:GetObject, pasa por un filtro previo llamado S3 Block Public Access. Este mecanismo existe desde 2018 precisamente para evitar exposiciones accidentales — y opera de forma independiente a las políticas IAM tradicionales.

Hay cuatro configuraciones dentro de Block Public Access. Las dos más relevantes para este escenario son:

  • BlockPublicAcls — Impide que se apliquen ACLs que otorguen acceso público.
  • IgnorePublicAcls — Ignora cualquier ACL pública existente, aunque esté configurada en el objeto.

Si IgnorePublicAcls está en true, poner el objeto en 'public-read' no tiene ningún efecto. AWS simplemente ignora esa ACL al evaluar la solicitud.

graph TD A["Solicitud Anónima
GET URL del objeto"] --> B{"Block Public Access
a nivel de CUENTA"} B -- "Activo" --> C["403 Access Denied"] B -- "Inactivo" --> D{"Block Public Access
a nivel de BUCKET"} D -- "Activo" --> E["403 Access Denied"] D -- "Inactivo" --> F{"¿Existe Bucket Policy
que permita s3:GetObject?"} F -- "Sí" --> G["200 OK — Acceso Permitido"] F -- "No" --> H{"¿ACL del objeto
es public-read?"} H -- "Sí y ACLs habilitadas" --> G H -- "No o ACLs desactivadas" --> I["403 Access Denied"]
  1. Solicitud anónima: Un navegador pide la URL pública del objeto S3.
  2. Filtro de cuenta: AWS evalúa primero si Block Public Access está activo a nivel de cuenta. Si bloquea, devuelve 403 inmediatamente.
  3. Filtro de bucket: Si la cuenta lo permite, evalúa el Block Public Access del bucket específico.
  4. Bucket Policy: Si ambos filtros pasan, evalúa si existe una bucket policy que permita s3:GetObject para Principal: "*".
  5. ACL del objeto: Como último recurso, evalúa la ACL del objeto (comportamiento legacy, cada vez más limitado).
Pensar en Block Public Access como un interruptor maestro de la instalación eléctrica: no importa cuántos enchufes individuales estén encendidos — si el interruptor principal está abierto, no hay corriente.

Diagnóstico: Por Qué S3 Devuelve 'Access Denied' en Acceso Público

Antes de tocar cualquier configuración, hay que identificar en qué capa está el bloqueo. Hay dos lugares donde Block Public Access puede estar activo: a nivel de cuenta y a nivel de bucket. Muchos operadores solo revisan el bucket y se olvidan de que la configuración de cuenta puede estar bloqueando todo.

Paso 1 — Verificar Block Public Access a nivel de cuenta

Este es el error más común que se pasa por alto. La configuración de cuenta aplica a todos los buckets, independientemente de lo que configure cada bucket individualmente. Si está activa aquí, nada más importa.

aws s3control get-public-access-block \
  --account-id 123456789012

Si la respuesta muestra algún valor en true, ese es tu problema raíz:

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

Paso 2 — Verificar Block Public Access a nivel de bucket

Aunque la cuenta no bloquee, cada bucket tiene su propia configuración independiente. Un bucket creado recientemente tiene todos estos valores en true por defecto.

aws s3api get-public-access-block \
  --bucket nombre-de-tu-bucket

Paso 3 — Verificar la Bucket Policy

Si Block Public Access está desactivado en ambas capas pero el acceso sigue fallando, el siguiente sospechoso es la ausencia de una bucket policy que permita explícitamente s3:GetObject para el público. Una ACL de objeto por sí sola puede no ser suficiente dependiendo de la configuración de Object Ownership del bucket.

aws s3api get-bucket-policy \
  --bucket nombre-de-tu-bucket

Si el comando devuelve NoSuchBucketPolicy, el bucket no tiene política — y si Block Public Access está desactivado pero no hay bucket policy, el acceso público puede seguir fallando según la configuración de Object Ownership.

Paso 4 — Verificar Object Ownership

Desde abril de 2023, AWS desactivó las ACLs de objetos para buckets nuevos por defecto mediante la configuración Object Ownership: BucketOwnerEnforced. Con esta configuración activa, las ACLs de objeto están completamente deshabilitadas — poner un objeto en 'public-read' no tiene ningún efecto operativo.

aws s3api get-bucket-ownership-controls \
  --bucket nombre-de-tu-bucket

Si la respuesta es BucketOwnerEnforced, las ACLs están desactivadas y debes usar exclusivamente una bucket policy para el acceso público.

graph LR A["403 Access Denied"] --> B["Paso 1: Revisar Block Public Access
a nivel de CUENTA"] B --> C["aws s3control get-public-access-block"] C --> D["Paso 2: Revisar Block Public Access
a nivel de BUCKET"] D --> E["aws s3api get-public-access-block"] E --> F["Paso 3: Revisar Bucket Policy"] F --> G["aws s3api get-bucket-policy"] G --> H["Paso 4: Revisar Object Ownership"] H --> I["aws s3api get-bucket-ownership-controls"] I --> J["Aplicar corrección
según capa identificada"]

Solución: Habilitar Acceso Público Correctamente

Hay dos rutas dependiendo de tu caso de uso. Para la mayoría de escenarios modernos, la Solución A (bucket policy sin ACLs) es la recomendada.

Solución A: Bucket Policy (Recomendada)

Esta es la forma correcta de exponer objetos públicamente en buckets nuevos. No requiere modificar Object Ownership ni habilitar ACLs.

Paso A1 — Desactivar Block Public Access en el bucket

aws s3api put-public-access-block \
  --bucket nombre-de-tu-bucket \
  --public-access-block-configuration \
    'BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false'

Paso A2 — Verificar que Block Public Access de cuenta no bloquee
Si el nivel de cuenta también bloquea, debes desactivarlo también (requiere permisos de administrador de cuenta):

aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
    'BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false'

⚠️ Modifica solo los valores necesarios. Si tu organización requiere que algunos permanezcan activos, ajusta solo los que bloquean tu caso de uso específico.

Paso A3 — Aplicar Bucket Policy para acceso público de lectura

🔽 Ver bucket policy completa
aws s3api put-bucket-policy \
  --bucket nombre-de-tu-bucket \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "PublicReadGetObject",
        "Effect": "Allow",
        "Principal": "*",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::nombre-de-tu-bucket/*"
      }
    ]
  }'

Solución B: ACL de Objeto (Legacy — solo si Object Ownership lo permite)

Si tu bucket tiene Object Ownership configurado como ObjectWriter o BucketOwnerPreferred, puedes usar ACLs. Primero desactiva Block Public Access (Pasos A1 y A2), luego:

aws s3api put-object-acl \
  --bucket nombre-de-tu-bucket \
  --key ruta/a/tu-imagen.jpg \
  --acl public-read

Esta aproximación aplica solo al objeto individual. Para múltiples objetos, la bucket policy de la Solución A es más mantenible.

El Error Clásico: 'Ya Lo Puse Público' — Experiencia de Campo

El escenario típico: alguien sube una imagen desde la consola de AWS, marca la opción 'Make public' durante la subida, y sigue recibiendo 403. La consola incluso muestra el objeto con el ícono de 'público'. Pero el acceso sigue fallando.

El diagnóstico inicial suele ser: 'la ACL no se aplicó correctamente'. Se intenta aplicar la ACL de nuevo, se verifica con get-object-acl, y confirma que está en public-read. Confusión total.

La causa real: IgnorePublicAcls estaba en true a nivel de bucket. La ACL existe en los metadatos del objeto — AWS la acepta y la almacena — pero la ignora completamente al evaluar solicitudes de acceso. El objeto aparece como 'público' en la consola porque tiene la ACL configurada, pero esa ACL nunca se evalúa.

La corrección no fue reconfigurar el objeto. Fue desactivar IgnorePublicAcls en el bucket, o mejor aún, migrar a una bucket policy y dejar las ACLs de lado.

La consola puede mostrar un objeto como 'público' basándose en su ACL configurada, sin reflejar si Block Public Access está ignorando esa ACL activamente.

Consideraciones de Seguridad y Cuándo NO Hacer Esto

Exponer un bucket S3 públicamente tiene implicaciones reales. Antes de desactivar Block Public Access, confirma que:

  • El bucket contiene únicamente contenido que debe ser público. Un bucket mixto (datos privados + assets públicos) es un riesgo operativo.
  • No tienes datos sensibles, backups, logs, o configuraciones en el mismo bucket.
  • Tienes habilitado S3 Server Access Logging o AWS CloudTrail para auditar accesos.

Para la mayoría de casos de uso de assets estáticos (imágenes, CSS, JS), la arquitectura recomendada es usar CloudFront con Origin Access Control (OAC) en lugar de acceso público directo al bucket. Esto mantiene el bucket privado y CloudFront actúa como intermediario autenticado.

Verificación Final del Acceso Público en S3

Después de aplicar los cambios, verifica que el acceso funciona correctamente:

# Verificar que Block Public Access está desactivado en el bucket
aws s3api get-public-access-block \
  --bucket nombre-de-tu-bucket

# Verificar la bucket policy aplicada
aws s3api get-bucket-policy \
  --bucket nombre-de-tu-bucket

# Probar acceso al objeto directamente
curl -I https://nombre-de-tu-bucket.s3.amazonaws.com/ruta/a/tu-imagen.jpg

Un HTTP 200 en el curl confirma que el acceso público está funcionando. Un 403 persistente después de estos cambios generalmente indica que Block Public Access sigue activo a nivel de cuenta.

Próximos Pasos y Recursos

Si necesitas acceso público a assets estáticos de forma escalable y segura, considera migrar a CloudFront + S3 con Origin Access Control — el bucket permanece privado y CloudFront gestiona la distribución pública. Para profundizar en el modelo de permisos de S3, la documentación oficial de S3 Access Control Overview y la referencia de Block Public Access son los puntos de partida correctos.

Glosario de Términos Clave

TérminoDefinición
Block Public AccessMecanismo de S3 que bloquea acceso público a nivel de cuenta o bucket, con prioridad sobre ACLs y bucket policies.
ACL (Access Control List)Mecanismo legacy de S3 para definir permisos a nivel de objeto o bucket. Desactivado por defecto en buckets nuevos.
Bucket PolicyPolítica IAM adjunta a un bucket S3 que controla acceso a nivel de bucket y objeto mediante JSON.
Object OwnershipConfiguración de bucket que controla si las ACLs de objeto están habilitadas y quién es el propietario de los objetos subidos.
Origin Access Control (OAC)Mecanismo de CloudFront para acceder a buckets S3 privados de forma autenticada, sin exponer el bucket públicamente.

Comentarios

Entradas populares de este blog

EC2 sin acceso a Internet en VPC personalizada: Internet Gateway y Route Table

Actualizar Contenido en CloudFront: Cómo Crear una Invalidación para Limpiar el Caché del Edge

Aumentar el Timeout de Lambda: Configuración, Límites y Diagnóstico en Producción