Lo que suele aparecer en una cuenta AWS no es sofisticado. Es lo que se dejó abierto “por un rato”: una clave de acceso vieja con permisos amplios, un bucket público, un puerto de base de datos abierto a internet o un registro de auditoría apagado. Esta es la lista que usamos como primer paso, con los comandos para revisarla.
Todo lo que sigue es de solo lectura y se ejecuta sobre su propia cuenta. Basta un usuario o rol con la política administrada SecurityAudit.
1. Usuario root: con MFA y sin claves
El usuario root no se puede limitar con políticas. No debería tener claves de acceso y debería tener MFA.
aws iam get-account-summary \
--query 'SummaryMap.{mfa:AccountMFAEnabled,claves:AccountAccessKeysPresent}'
Lo correcto es mfa: 1 y claves: 0. Si hay claves de root, elimínelas después de confirmar que nada las usa.
2. IAM: claves antiguas y usuarios sin uso
El informe de credenciales muestra, por usuario, la antigüedad y el último uso de cada clave y contraseña.
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text \
| base64 --decode > credenciales.csv
En ese archivo, busque claves activas de más de 90 días, claves que nunca se usaron y usuarios sin actividad reciente. Las personas deberían entrar con SSO o roles, no con claves permanentes; los servicios, con roles de IAM.
Revise también las políticas propias con comodines:
for arn in $(aws iam list-policies --scope Local --query 'Policies[].Arn' --output text); do
v=$(aws iam get-policy --policy-arn "$arn" --query 'Policy.DefaultVersionId' --output text)
aws iam get-policy-version --policy-arn "$arn" --version-id "$v" \
--query 'PolicyVersion.Document' --output json | grep -q '"\*"' && echo "$arn"
done
No todo "*" es un error, pero cada uno merece una explicación. IAM Access Analyzer ayuda con los accesos desde fuera de la cuenta.
3. S3: acceso público
Primero, el bloqueo de acceso público a nivel de cuenta, que protege incluso si alguien se equivoca en un bucket:
aws s3control get-public-access-block \
--account-id "$(aws sts get-caller-identity --query Account --output text)"
Las cuatro opciones deberían estar en true, salvo que tenga un sitio estático servido directamente desde S3; en ese caso conviene moverlo detrás de CloudFront con acceso de origen controlado. Luego, bucket por bucket:
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
echo "$b $(aws s3api get-bucket-policy-status --bucket "$b" \
--query 'PolicyStatus.IsPublic' --output text 2>/dev/null)"
done
Esta consulta solo detecta los buckets públicos por política. En buckets creados antes de abril de 2023 también puede haber acceso público por ACL: revíselo con aws s3api get-bucket-acl --bucket <nombre> o con IAM Access Analyzer para S3.
4. Security groups abiertos a internet
Puertos de base de datos, SSH o RDP abiertos a todo internet son una de las causas más comunes de compromiso. Revise IPv4 e IPv6:
aws ec2 describe-security-groups \
--filters Name=ip-permission.cidr,Values=0.0.0.0/0 \
--query 'SecurityGroups[].{id:GroupId,nombre:GroupName,puertos:IpPermissions[].FromPort}'
aws ec2 describe-security-groups \
--filters Name=ip-permission.ipv6-cidr,Values=::/0 \
--query 'SecurityGroups[].{id:GroupId,nombre:GroupName,puertos:IpPermissions[].FromPort}'
Los puertos 80 y 443 en un balanceador son normales. El 22, el 3389, el 3306, el 5432, el 6379 o el 27017 abiertos a todo internet no lo son. Para administrar servidores, use Session Manager en lugar de abrir SSH.
Este comando revisa una sola región: repítalo en cada región que use.
5. CloudTrail y GuardDuty
Sin registro de auditoría no hay forma de saber qué pasó después de un incidente.
aws cloudtrail describe-trails \
--query 'trailList[].{nombre:Name,multiregion:IsMultiRegionTrail}'
aws cloudtrail get-trail-status --name <trail> --query IsLogging
aws guardduty list-detectors
describe-trails muestra que el trail existe; get-trail-status confirma que está registrando (true). Conviene tener al menos un trail multirregión activo, con los registros en un bucket protegido, y GuardDuty encendido en las regiones que usa. GuardDuty tiene costo, que en cuentas pequeñas suele ser bajo; revise la estimación en la consola antes de activarlo en todas las regiones.
6. Secretos en texto plano
Las variables de entorno de las definiciones de tarea de ECS y de las funciones Lambda se ven en la consola y en la API. No son un lugar para contraseñas.
aws ecs describe-task-definition --task-definition <familia> \
--query 'taskDefinition.containerDefinitions[].environment'
aws lambda get-function-configuration --function-name <función> \
--query 'Environment.Variables'
Si aparecen contraseñas, tokens o cadenas de conexión, muévalos a Secrets Manager o Parameter Store y referéncielos desde la definición de tarea con secrets. Después, rótelos: un secreto que estuvo visible se considera expuesto. Revise también el repositorio con una herramienta como gitleaks, porque el mismo valor suele estar en el historial.
7. Snapshots e imágenes compartidas públicamente
Un snapshot público de un disco o de una base de datos es una copia completa de sus datos al alcance de cualquiera.
aws ec2 describe-snapshots --owner-ids self --restorable-by-user-ids all \
--query 'Snapshots[].SnapshotId'
aws ec2 describe-images --owners self --filters Name=is-public,Values=true \
--query 'Images[].ImageId'
La respuesta correcta para los dos es una lista vacía. Revise también los snapshots de RDS con describe-db-snapshot-attributes.
Qué hacer con lo que encuentre
Ordene por exposición: primero lo que es accesible desde internet sin credenciales (buckets, snapshots, puertos), después las credenciales con privilegios (root, claves antiguas con permisos amplios) y al final la detección (CloudTrail, GuardDuty). Antes de cerrar algo en producción, confirme qué lo usa: cerrar un security group que un servicio necesitaba también es un incidente.
Esta lista es un punto de partida y no reemplaza una revisión completa, que lee una por una las relaciones de confianza entre roles y busca rutas de escalamiento de privilegios. Pero cubre lo que más se repite.
Las técnicas de esta nota se aplican sobre sistemas de los que usted es titular o con permiso por escrito del titular. Probar sistemas ajenos sin permiso es un delito en Ecuador (COIP, art. 234) y en la mayoría de los países.
Más notas
¿Quiere recibir las próximas? Siga el RSS.