AWS security compliance IAM audit

Cómo hacer una auditoría de seguridad en AWS: guía práctica para equipos DevOps

Procedimiento de auditoría de seguridad de un entorno AWS: desde la revisión de IAM, pasando por la configuración de red, hasta el cifrado y el monitoreo. Un checklist listo para evaluaciones periódicas.

Jerzy Kopaczewski ·
Su entorno AWS crece y hace meses que nadie hace una revisión de seguridad. La cuenta root tiene claves de API, hay Security Groups con puertos abiertos y CloudTrail escribe en un bucket que nadie monitorea. Es momento de una auditoría sistemática.

Si necesita apoyo externo para ejecutar una auditoría de seguridad cloud, consulte nuestro servicio de consultoría de DevOps y seguridad cloud.

Cuándo ejecutar una auditoría

  • Antes de migrar producción a una cuenta AWS nueva
  • Después de un incidente de seguridad o una brecha sospechada
  • En un ciclo trimestral (cada seis meses como mínimo)
  • Antes de una auditoría de cumplimiento (SOC 2, ISO 27001, NIS2)
  • Después de cambios significativos en la infraestructura (nueva VPC, nueva cuenta en la organización)

Herramientas de automatización

Antes de empezar una revisión manual, ejecute escáneres automatizados:

# AWS Security Hub - comprobación de cumplimiento nativa
aws securityhub get-findings --filters '{"RecordState":[{"Value":"ACTIVE","Comparison":"EQUALS"}]}' \
  --query 'Findings[?Severity.Label==`CRITICAL`].[Title,Resources[0].Id]' --output table

# Prowler - auditoría de AWS open-source (instalación: pip install prowler)
prowler aws --severity critical high --output-formats json-ocsf html

# ScoutSuite - auditoría de seguridad multi-cloud
scout aws --report-dir ./audit-$(date +%Y%m%d)

# AWS Trusted Advisor (requiere soporte Business/Enterprise)
aws support describe-trusted-advisor-checks --language en \
  --query 'checks[?category==`security`].name'

Prowler cubre más de 300 comprobaciones de CIS AWS Benchmark, PCI-DSS y GDPR. Si solo puede ejecutar una herramienta, elija Prowler.

Checklist: IAM y acceso

Cuenta root

# Comprobar si root tiene claves de API (NO debería tenerlas)
aws iam get-account-summary --query 'SummaryMap.AccountAccessKeysPresent'
# Resultado esperado: 0

# Comprobar si el MFA está activado en root
aws iam get-account-summary --query 'SummaryMap.AccountMFAEnabled'
# Resultado esperado: 1
Punto de controlEstado esperadoCómo comprobarlo
La cuenta root no tiene claves de APIAccountAccessKeysPresent = 0aws iam get-account-summary
MFA en la cuenta rootAccountMFAEnabled = 1aws iam get-account-summary
Cuenta root sin uso durante >90 díasSin inicio de sesión recienteCredential Report
MFA en todos los usuarios IAM con acceso a consolaCobertura del 100%aws iam generate-credential-report
Sin políticas inline en los usuariosTodo a través de grupos/rolesprowler aws -c iam_inline_policy_no_administrative_privileges
Rotación de claves de API <90 díasSin claves obsoletasColumna access_key_last_rotated del Credential Report

Políticas y roles

# Encontrar usuarios con AdministratorAccess
aws iam list-entities-for-policy \
  --policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
  --query '[PolicyUsers[].UserName, PolicyGroups[].GroupName, PolicyRoles[].RoleName]'

# Encontrar políticas con comodín (*) en acciones y recursos
aws iam list-policies --scope Local --query 'Policies[].Arn' --output text | \
  xargs -I {} aws iam get-policy-version --policy-arn {} \
    --version-id $(aws iam get-policy --policy-arn {} --query 'Policy.DefaultVersionId' --output text) \
    --query 'PolicyVersion.Document'

# Roles sin uso (sin assume en los últimos 90 días)
aws iam generate-service-last-accessed-details --arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME

Checklist: red y acceso

# Security Groups con 0.0.0.0/0 abierto en puertos distintos de 80/443
aws ec2 describe-security-groups \
  --filters Name=ip-permission.cidr,Values='0.0.0.0/0' \
  --query 'SecurityGroups[].{Name:GroupName,ID:GroupId,Rules:IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]].{Port:FromPort,Proto:IpProtocol}}' \
  --output table

# Subredes públicas sin NAT Gateway (posible vector de ataque)
aws ec2 describe-route-tables --query 'RouteTables[].Routes[?GatewayId!=null && DestinationCidrBlock==`0.0.0.0/0`]'

# VPC Flow Logs - comprobar si están activados en todas las VPC
aws ec2 describe-flow-logs --query 'FlowLogs[].{VPC:ResourceId,Status:FlowLogStatus,Dest:LogDestination}'
Punto de controlEstado esperado
Ningún SG con 0.0.0.0/0 en SSH (22) o RDP (3389)Cero resultados
VPC Flow Logs activados en cada VPC100%
Sin IP públicas en instancias de base de datosCero resultados
NACL que bloqueen rangos de IP maliciosos conocidosConfigurado
Ninguna VPC por defecto en uso para producciónEliminada o vacía

Checklist: cifrado y almacenamiento

# Buckets S3 sin cifrado por defecto
aws s3api list-buckets --query 'Buckets[].Name' --output text | tr '\t' '\n' | while read bucket; do
  enc=$(aws s3api get-bucket-encryption --bucket "$bucket" 2>&1)
  if echo "$enc" | grep -q "ServerSideEncryptionConfigurationNotFoundError"; then
    echo "SIN CIFRADO: $bucket"
  fi
done

# Buckets S3 con acceso público
aws s3api list-buckets --query 'Buckets[].Name' --output text | tr '\t' '\n' | while read bucket; do
  status=$(aws s3api get-public-access-block --bucket "$bucket" 2>&1)
  if echo "$status" | grep -q "NoSuchPublicAccessBlockConfiguration"; then
    echo "SIN BLOQUEO DE ACCESO PÚBLICO: $bucket"
  fi
done

# Volúmenes EBS sin cifrado
aws ec2 describe-volumes --filters Name=encrypted,Values=false \
  --query 'Volumes[].{ID:VolumeId,Size:Size,State:State}' --output table

# RDS sin cifrado en reposo
aws rds describe-db-instances \
  --query 'DBInstances[?StorageEncrypted==`false`].{ID:DBInstanceIdentifier,Engine:Engine}' --output table

Checklist: logging y monitoreo

# CloudTrail - comprobar si está activo y es multi-región
aws cloudtrail describe-trails --query 'trailList[].{Name:Name,MultiRegion:IsMultiRegionTrail,Logging:HasCustomEventSelectors}'
aws cloudtrail get-trail-status --name default --query '{IsLogging:IsLogging,LatestDelivery:LatestDeliveryTime}'

# Config - comprobar si AWS Config está registrando
aws configservice describe-configuration-recorders \
  --query 'ConfigurationRecorders[].{Name:name,Recording:recording}'

# GuardDuty - comprobar si está activado
aws guardduty list-detectors --query 'DetectorIds'

# Alarmas en eventos clave (login de root, cambios en políticas IAM)
aws cloudwatch describe-alarms --alarm-name-prefix "Security-" \
  --query 'MetricAlarms[].{Name:AlarmName,State:StateValue}'
Punto de controlEstado esperado
CloudTrail activo, multi-regiónIsLogging: true, Multi-región: true
CloudTrail registra en una cuenta separada (log archive)Bucket en una cuenta distinta
AWS Config registrando cambiosRecording: true
GuardDuty activado en cada regiónDetectorId en cada región
Alarma en el login de rootConfigurada y activa
Alarma en CreateUser/AttachPolicyConfigurada y activa

Cómo reportar los resultados

Después de ejecutar Prowler, genere un reporte:

# Reporte completo con priorización
prowler aws --severity critical high medium \
  --output-formats html csv json-ocsf \
  --output-directory ./audit-report-$(date +%Y%m%d)

# Comparar con la auditoría anterior
prowler aws --severity critical high \
  --output-formats csv \
  | diff previous-audit.csv - > delta-findings.txt

El reporte debería clasificar los hallazgos:

  1. Críticos - corregir en 24 horas (claves root abiertas, bases de datos públicas)
  2. Altos - corregir en una semana (MFA ausente, cifrado ausente)
  3. Medios - planificar para el siguiente sprint (rotación de claves, roles sin uso)

Automatizar auditorías periódicas

Programe la auditoría como un pipeline periódico (por ejemplo, semanal en CI/CD):

# .github/workflows/security-audit.yml
name: Weekly AWS Security Audit
on:
  schedule:
    - cron: '0 6 * * 1'  # lunes 6:00 UTC
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Prowler
        run: pip install prowler
      - name: Run audit
        run: prowler aws --severity critical high --output-formats json-ocsf html
        env:
          AWS_ACCESS_KEY_ID: $
          AWS_SECRET_ACCESS_KEY: $
          AWS_DEFAULT_REGION: eu-central-1
      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: security-audit-$
          path: output/

El rol IAM para la auditoría debería tener la política gestionada SecurityAudit + ViewOnlyAccess, sin permisos de escritura.

¿Ya hizo esta auditoría por su cuenta? Formalícela con un Well-Architected Review.

Una revisión de arquitectura gratuita de 30 minutos cubre seguridad, costo y fiabilidad en un formato estructurado, y hace que sus hallazgos califiquen para el descuento del 10% de AWS sobre la remediación. No se necesita acceso para la conversación inicial.

Próximos pasos

  • Corregir de inmediato los hallazgos críticos
  • Implementar AWS Config Rules para monitoreo continuo
  • Considerar AWS Organizations con SCP (Service Control Policies) como guardrails
  • Configurar Security Hub con agregación automática de hallazgos

Si necesita ayuda para ejecutar una auditoría o implementar automatización de seguridad, póngase en contacto.