ENTREGABLE DE MUESTRA · datos solo ilustrativos. Los nombres de organizaciones, las direcciones IP, los IDs de suscripción/tenant y todos los demás identificadores son marcadores ficticios (Contoso y RFC 5737 rangos de documentación) y no se refieren a ningún cliente ni infraestructura reales.
Fig. 02 · Evaluación de seguridad de Azure · Contoso PayGo thinsky.com

Auditoría de solo lectura · Contoso PayGo · suscripción a1b2c3d4

Postura de seguridad de Azure, medida frente a CIS, MCSB y NIST.

Secure Score 16,8 / 36 — 46,67 %. Veinte hallazgos en redes, identidad, detección, copias de seguridad y gobernanza. Uno crítico, nueve altos, cinco medios, tres bajos, dos informativos. El entorno es funcional; las capas de detección y de rutas de acceso no lo son.

Fecha de auditoría
2026-05-14
Capturado el
2026-05-14T20:13Z
Suscripción
Contoso PayGo
Tenant
e7f8a9b0-2222-4b33-8d44-1e55f6607b88
Identidad de auditoría
Service principal · Reader
Marcos
CIS Azure v2.1.0 · NIST 800-53 r5 · MCSB

Esta es una auditoría de solo lectura sin autoridad de remediación. Los hallazgos incluyen guía de remediación a nivel de código, pero Dana (Owner de la suscripción) ejecuta los cambios. El esfuerzo, la secuenciación y la programación se definen en § 8 Secuenciación recomendada.

§ 1 · Alcance

Qué entró en la auditoría — y qué no.

Tenante7f8a9b0-2222-4b33-8d44-1e55f6607b88
SuscripciónContoso PayGo (a1b2c3d4-1111-4a22-9c33-0d44e55f6a77)
Grupos de recursos en alcanceProduction-RG (eastus) + NetworkWatcherRG + azurebackuprg_eastus_1 (gestionado por Microsoft)
Marcos de cumplimientoCIS Azure Foundations v2.1.0 · NIST 800-53 r5 · Microsoft Cloud Security Benchmark
Tipo de servicioAuditoría de solo lectura, sin autoridad de remediación
Identidad de auditoríaApp id del service principal 0c1d2e3f-…, ámbito Reader a nivel de suscripción
Límites de la identidad de auditoríaSin Microsoft.Network/networkInterfaces/effectiveNetworkSecurityGroup/action, sin Microsoft.Compute/virtualMachines/assessPatches/action, sin Directory.Read.All en Microsoft Graph
Marca de tiempo de captura2026-05-14 20:13 UTC
Tipos de recursos en alcance18 (desglose completo en § 3)
Fuera de alcanceConfiguración del SO invitado, configuración a nivel de aplicación dentro de las VM, datos de directorio de Entra ID (el SP carece de ámbitos)

§ 2 · Resumen ejecutivo

Funcional, pero las capas de detección y de rutas de acceso no lo son.

16,8 / 36
Secure Score (Defender for Cloud) — 46,67 %. 48 de 143 evaluaciones están en Unhealthy. El entorno es funcional pero opera casi por completo en el nivel gratuito de Defender for Cloud, expone puertos del plano de gestión (RDP y SQL 1433) directamente a internet desde IPs domésticas individuales de administradores, no tiene diagnostic settings ni enrutamiento del Activity Log en ningún recurso, y tiene cero alertas de detección. Un nuevo despliegue de Azure Bastion con Shareable Link habilitado amplía de forma material la ruta de acceso — las sesiones pueden entregarse por URL, eludiendo Conditional Access en el extremo receptor.

Panel de severidad

Crítico1SQL 1433 alcanzable desde internet
Alto9RDP, Bastion, Defender, registro de eventos, alertas, copias de seguridad, DNS, DC, autenticación VPN
Medio5Flow logs, Key Vault, locks, exceso de Owners, identidad administrada
Bajo3DDoS Standard, conector roto, programación deshabilitada
Info2Lectura de Entra denegada, cifrado gestionado por la plataforma

8 riesgos principales — ordenados por explotabilidad × impacto

#HallazgoSeveridadQué gana el atacante
1SQL Server 1433 alcanzable directamente desde el internet públicoCríticoAcceso TDS directo a cada base de datos en DBSvr-001 desde cualquier host que comprometa la IP de origen del administrador — lectura/escritura con el privilegio que porte la credencial capturada, sin más controles delante del motor de base de datos.
2Cada VM tiene una IP pública con RDP abierto desde una IP doméstica de administradorAltoSuperficie de fuerza bruta / repetición de credenciales en cada una de las seis VM concentrada tras una única IP residencial; un solo compromiso de la red doméstica habilita intentos de inicio de sesión interactivo de Windows contra toda la flota.
3El Shareable Link de Azure Bastion está HABILITADOAltoCualquiera con una sesión de Azure puede generar una URL que otorga acceso a una VM objetivo sin que ese destinatario requiera evaluación de AAD / CA.
4Defender for Cloud — todos los planes de carga de trabajo están en GRATISAltoSin protección frente a amenazas en VM, SQL, Storage, Key Vault, ARM, DNS. Sin evaluación de vulnerabilidades. Sin JIT. Sin detección de comportamiento.
5Sin diagnostic settings en ningún lugar — el Activity Log no se exportaAltoLa línea de tiempo forense llega como máximo a 90 días de retención predeterminada de la plataforma; nada inmutable. Una brecha descubierta en el día 91 no tiene rastro de evidencia.
6Cero alertas del Activity LogAltoSin detección de cambios en asignaciones de roles, eliminación de reglas de NSG, destrucción de Key Vault, eliminación de vaults, etc.
7El Recovery Services Vault no tiene inmutabilidad ni autorización multiusuarioAltoManual de ransomware: cifrar VM + eliminar copias de seguridad (sujeto solo a la ventana de soft-delete de 14 días, que el mismo RBAC puede acortar).
8Los controladores de dominio y el servidor SQL tienen IPs públicas (violación de CIS 6.6)AltoLos DC y la capa de base de datos nunca deberían ser alcanzables desde internet, ni siquiera con acotamiento de NSG. El radio de impacto de una mala configuración del plano de control es catastrófico.
Corolario
operativo

El gateway VPN existe (Azure-ContosoNet, SKU VpnGw2AZ) pero su autenticación de cliente P2S está sin configurar (cero root certs, sin RADIUS, sin tenant de Entra ID definido). El túnel site-to-site ContosoNet está aprovisionado pero muestra 0 bytes de entrada / 0 bytes de salida. Los trabajadores remotos solo pueden llegar a Azure por la ruta RDP de IP pública. Por eso hoy no se pueden eliminar las IPs públicas — y la única forma legítima de eliminarlas en el futuro es configurar primero la autenticación P2S de Entra ID.

§ 3 · Inventario de la suscripción al momento de la auditoría

Dieciocho tipos de recursos — generados mediante Resource Graph.

Resources | summarize count() by type

Tipo de recursoCantidadNotas
microsoft.compute/disks10Discos de SO + datos
microsoft.network/publicipaddresses98 asociadas a recursos alcanzables desde internet
microsoft.compute/virtualmachines6Todas en ejecución, todas unidas al dominio
microsoft.network/networkinterfaces6Una por VM
microsoft.network/networksecuritygroups5Con ámbito de NIC, sin NSG con ámbito de subred
microsoft.compute/restorepointcollections4Gestionadas por Azure Backup
microsoft.compute/virtualmachines/extensions3SqlIaasExtension, AzureBackupWindowsWorkload, AADLogin
microsoft.sqlvirtualmachine/sqlvirtualmachines1Extensión de SQL VM de DBSvr-001
microsoft.network/connections1S2S ContosoNet (inactivo — 0 bytes a través de él)
microsoft.network/localnetworkgateways1Definición del gateway on-premises (198.51.100.60, 192.168.32.0/24)
microsoft.network/virtualnetworkgateways1Azure-ContosoNet (VpnGw2AZ)
microsoft.devtestlab/schedules1Apagado automático de ADM-001az — deshabilitado
microsoft.web/connections1Conector "azurevm" de Logic Apps — estado Error
microsoft.compute/availabilitysets1
microsoft.recoveryservices/vaults1Production-Vault (eran 2; vault-dbfiles eliminado hoy)
microsoft.network/networkwatchers1NetworkWatcher_eastus
microsoft.network/bastionhosts1Contoso_Bastion SKU Standard — NUEVO respecto a la auditoría anterior
microsoft.network/virtualnetworks1vNet-001 (10.0.2.0/24)
microsoft.storage/storageaccounts0contosostorageacct1 fue eliminada hoy entre ejecuciones de la auditoría

§ 4 · Topología de red

La ruta hacia internet, diagramada.

                                    INTERNET
                                       │
       ┌───────────────────────────────┼───────────────────────────────────┐
       │                               │                                   │
       ▼                               ▼                                   ▼
┌──────────────────┐         ┌───────────────┐               ┌──────────────────────┐
│ 8 VM public IPs  │         │ Contoso_Bastion   │               │ Azure-ContosoNet (VPN)   │
│ (Standard, Static│         │ Standard SKU  │               │ VpnGw2AZ Gen2 a/a    │
│  Regional)       │         │ 2 scale units │               │ 2 public IPs         │
│                  │         │ ShareableLink │               │  • S2S idle (ContosoNet) │
│ ports per VM:    │         │   : ENABLED   │               │    to 198.51.100.60  │
│   3389 from      │         │ IpConnect:    │               │  • P2S protocols     │
│   198.51.100.10   │         │   ENABLED     │               │    configured but    │
│   (admin home)   │         │ Tunneling:    │               │    auth UNCONFIGURED │
│                  │         │   ENABLED     │               │    (0 root certs, no │
│ DCs + LAN also   │         │               │               │    RADIUS, no AAD)   │
│ open 53/TCP+UDP  │         │ subnet:       │               │                      │
│ to 0.0.0.0/0     │         │  AzureBastion-│               │ Used today:          │
│                  │         │  Subnet       │               │   NO (0 bytes)       │
│ DBSvr also open  │         │               │               │                      │
│ 1433 to one IP   │         │               │               │                      │
└────────┬─────────┘         └──────┬────────┘               └──────────┬───────────┘
         │                          │                                   │
         └──────────────────────────┴───────────────────────────────────┘
                                       ▼
              ┌────────────────────────────────────────────────────┐
              │ vNet-001 (10.0.2.0/24)                             │
              │   no peerings, no route tables (default routing)   │
              │   no DDoS Standard, no Flow Logs                   │
              │                                                    │
              │  10.0.2.4  DCSvr-001   DomainControllers-NSG       │
              │  10.0.2.5  DCSvr-002   DomainControllers-NSG       │
              │  10.0.2.6  LANSvr-001  Servers-NSG                 │
              │  10.0.2.7  DBSvr-001   DBServers-NSG               │
              │  10.0.2.8  ACC-001az   ACC-001az-nsg               │
              │  10.0.2.9  ADM-001az   ADM-001az-nsg               │
              │                                                    │
              │  Default rule AllowVNetInBound permits ALL traffic │
              │  between any two VMs on any port (no microsegment) │
              └────────────────────────────────────────────────────┘

Reglas de NSG de entrada — lista completa

NSG (ámbito de NIC)PriDirAcciónProtoOrigenDstPuerto dstRegla
ACC-001az-nsg300InboundAllowTCP198.51.100.10*3389PermitRDP
ADM-001az-nsg100InboundAllowTCP198.51.100.10*3389PermitRDP
DBServers-NSG100InboundAllowTCP198.51.100.10, 198.51.100.11*3389PermitMyRDP
DBServers-NSG110InboundAllowTCP198.51.100.10*1433PermitMySQL
DomainControllers-NSG100InboundAllowTCP198.51.100.10*3389PermitMyRDP
DomainControllers-NSG1010InboundAllowTCP**53PermitDNS-TCP
DomainControllers-NSG1020InboundAllowUDP**53PermitDNS-UDP
Servers-NSG100InboundAllowTCP198.51.100.10*3389PermitMyRDP
Servers-NSG1010InboundAllowTCP**53PermitDNS-TCP
Servers-NSG1020InboundAllowUDP**53PermitDNS-UDP

Reglas de NSG de salida: ninguna definida. Todo el tráfico de salida se rige por la regla predeterminada de Azure AllowInternetOutBound — cada VM puede comunicarse con cualquier destino de internet en cualquier puerto sin restricción. No hay filtrado de egress, ni Azure Firewall, ni NAT Gateway, ni Application Gateway.

IPs públicas — a qué están asociadas

NombreIPRecursoPor qué
AzureVPN-PIP1203.0.113.21Azure-ContosoNet (VPN)Requerida para el VPN GW
AzureVPN-PIP2203.0.113.22Azure-ContosoNet (VPN activo-activo)Requerida para el VPN GW
vNet-001-IPv4203.0.113.41Contoso_BastionRequerida para Bastion
ACC-001az-ip203.0.113.31NIC de ACC-001azEliminable una vez completada la adopción de Bastion
ADM-001az-ip203.0.113.32NIC de ADM-001azEliminable una vez completada la adopción de Bastion
DBSvr-001-ip203.0.113.33NIC de DBSvr-001Debe eliminarse de inmediato — capa de BD
DCSvr-001-ip203.0.113.11NIC de DCSvr-001Debe eliminarse — capa de DC
DCSvr-002-ip203.0.113.12NIC de DCSvr-002Debe eliminarse — capa de DC
LANSvr-001-ip203.0.113.34NIC de LANSvr-001Debe eliminarse

La presencia de Bastion significa que cada IP pública directa de VM ahora es redundante — el acceso administrativo puede enrutarse a través de Contoso_Bastion sobre su sesión SSL. Eliminarlas elimina seis superficies de fuerza bruta (más la superficie de SQL 1433) sin romper el acceso.

§ 5 · Hallazgos

Veinte hallazgos — cada uno reproducible desde scripts/_security_audit.json.

F-01Crítico CVSS v3.1 9,4 · AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

SQL Server TCP/1433 alcanzable desde el internet público.

Recurso
DBServers-NSG regla PermitMySQL · NIC dbsvr-001887 (VM DBSvr-001, PIP 203.0.113.33)
Control
CIS Azure Foundations v2.1.0 § 6.4 · MCSB NS-1 · NIST 800-53 SC-7

Evidencia

$ python scripts/azure_security_audit.py | jq '.network.nsgs[] | select(.name=="DBServers-NSG").rules'
[{ "name":"PermitMySQL","priority":110,"direction":"Inbound","access":"Allow",
   "protocol":"TCP","srcAddr":"198.51.100.10","dstPort":"1433" }]

Compromiso

Un compromiso de la única IP de origen 198.51.100.10 (la IP doméstica de internet de un administrador) otorga acceso TCP directo a TDS 1433 en el SQL Server. Desde ahí el atacante puede: (a) intentar fuerza bruta de inicio de sesión SQL / Windows contra DBSvr-001 sin ningún límite de velocidad ni WAF en la ruta; (b) hacer fingerprinting y atacar CVE sin parchear de SQL Server (el motor es alcanzable desde hosts arbitrarios de internet en el momento en que la IP de origen se compromete o se reasigna); (c) ante cualquier autenticación exitosa, leer, modificar, eliminar o exfiltrar cualquier objeto de base de datos contra el que esté autorizada la credencial capturada — la auditoría no puede indicarle qué bases de datos contienen qué, por lo que la cota superior es "cada base de datos alojada en esta instancia"; (d) pivotar desde xp_cmdshell u otras funciones similares, si están habilitadas, para lograr ejecución de código en la VM. Las IPs residenciales suelen ser reasignadas por los ISP y con frecuencia están detrás de routers de consumo comprometidos.

Remediación · Azure CLI

# Paso 1 — confirmar que nadie depende actualmente de la ruta pública 1433
# (el Activity Log + los flow logs lo mostrarían; sin ambos, asumir que no)

# Paso 2 — eliminar la regla
az network nsg rule delete \
  --resource-group Production-RG \
  --nsg-name DBServers-NSG \
  --name PermitMySQL

# Paso 3 — confirmar que la ruta de acceso permanece: ACC-001az (10.0.2.8) → DBSvr-001 (10.0.2.7) en 1433
# está permitida por la regla predeterminada AllowVnetInBound. El CRM sigue funcionando.

Reversión: az network nsg rule create --resource-group Production-RG --nsg-name DBServers-NSG --name PermitMySQL --priority 110 --direction Inbound --access Allow --protocol Tcp --source-address-prefixes 198.51.100.10 --destination-port-ranges 1433

Validación

az network nsg rule list --resource-group Production-RG --nsg-name DBServers-NSG \
  --query "[?name=='PermitMySQL']" -o table
# Salida esperada: vacía

Radio de impacto de la corrección · Medio · reversible en segundos. Verifique con Dana que ninguna herramienta de reportes se conecte a la SQL VM desde fuera de la VNet por la IP pública.

F-02Alto CVSS v3.1 8,1 · AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

RDP público (TCP/3389) en cada VM desde una única IP residencial de administrador.

Recursos
Las 6 NSG permiten TCP/3389 de entrada desde 198.51.100.10 (una también desde 198.51.100.11). PIPs: ACC-001az-ip, ADM-001az-ip, DBSvr-001-ip, DCSvr-001-ip, DCSvr-002-ip, LANSvr-001-ip.
Control
CIS Azure Foundations v2.1.0 § 6.1 · MCSB NS-2 · NIST 800-53 SC-7

Evidencia

$ jq '.network.nsgs[].rules[] | select(.dstPort=="3389")' < scripts/_security_audit.json
# Cada NSG devuelve al menos una regla de permiso

Compromiso

Una única IP residencial 198.51.100.10 (hogar del administrador) es la pieza clave de movimiento lateral para toda la flota de producción. Los compromisos comunes de routers domésticos (CVE-2024-3273 D-Link, MikroTik VPNFilter, 0-days de ISP-CPE) producen man-in-the-middle en el tráfico de salida y permitirían fuerza bruta o pass-the-hash desde una posición cuya IP de origen la NSG ya confía. Incluso sin compromiso, un ISP residencial puede reasignar esta IP a otro suscriptor tras el reinicio de un módem. Un inicio de sesión RDP exitoso en cualquier VM de esta subred proporciona entonces una posición dentro de la VNet desde la cual la regla predeterminada de Azure AllowVnetInBound permite movimiento este-oeste sin restricción hacia cualquier otra VM, incluidas la SQL VM y ambos controladores de dominio (véase § 4 — no hay microsegmentación).

Remediación · Azure CLI

# REQUISITO PREVIO: Bastion está aprovisionado (Contoso_Bastion existe) — verifique el acceso vía Bastion
# a ACC-001az y ADM-001az ANTES de eliminar la ruta de IP pública.

# Paso 1 — Verificar que la ruta de bastion funciona para al menos un administrador
# (verificación manual — Dana entra por RDP a través de https://portal.azure.com → Bastion)

# Paso 2 — Eliminar las reglas RDP por NSG
for nsg in ACC-001az-nsg ADM-001az-nsg DBServers-NSG DomainControllers-NSG Servers-NSG; do
  az network nsg rule list -g Production-RG --nsg-name $nsg \
    --query "[?destinationPortRange=='3389'].name" -o tsv | \
    xargs -I{} az network nsg rule delete -g Production-RG --nsg-name $nsg --name {}
done

# Paso 3 — Desasociar las 6 IPs públicas administrativas de sus NIC
for ip in ACC-001az-ip ADM-001az-ip DBSvr-001-ip DCSvr-001-ip DCSvr-002-ip LANSvr-001-ip; do
  nic_ip_config=$(az network public-ip show -g Production-RG -n $ip --query ipConfiguration.id -o tsv)
  nic=$(echo $nic_ip_config | awk -F'/' '{print $9}')
  cfg=$(echo $nic_ip_config | awk -F'/' '{print $11}')
  az network nic ip-config update -g Production-RG --nic-name $nic --name $cfg --remove publicIpAddress
done

# Paso 4 — Opcionalmente eliminar los objetos de IP pública tras la desasociación
# (hágalo tras una retención suave de 30 días para confirmar que no hace falta reversión)

Reversión: vuelva a crear las reglas de NSG (prioridad 100, src 198.51.100.10, puerto 3389), luego reasocie cada PIP al ipconfig1 de su NIC.

Validación

az network nsg rule list -g Production-RG --nsg-name DBServers-NSG \
  --query "[?destinationPortRange=='3389']" -o table
# Esperado: vacío en las 5 NSG

Radio de impacto de la corrección · Alto · el acceso administrativo depende por completo de que Bastion funcione. No lo ejecute sin una ruta de Bastion verificada para cada operador Y una alternativa break-glass verificada (p. ej., la IP de Dana en una allowlist temporal por 48 horas).

F-03Alto CVSS v3.1 7,5 · AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

El enableShareableLink de Azure Bastion está en true.

Recurso
/subscriptions/a1b2c3d4-…/resourceGroups/Production-RG/providers/Microsoft.Network/bastionHosts/Contoso_Bastion
Control
MCSB IM-1, IM-3 · NIST 800-53 IA-2(1) · CIS Azure § 6.x (a nivel de red)

Evidencia

$ jq '.bastion_hosts_full.value[0].properties | {enableShareableLink, enableIpConnect, enableTunneling, enableKerberos, scaleUnits}' < scripts/_security_extra.json
{
  "enableShareableLink": true,
  "enableIpConnect":     true,
  "enableTunneling":     true,
  "enableKerberos":      false,
  "scaleUnits":          2
}

Compromiso

Shareable Link permite que un usuario de Azure con el permiso adecuado de Bastion genere una URL que otorga acceso RDP / SSH a una VM específica. La URL puede reenviarse a cualquiera — incluidos usuarios sin cuenta de Entra ID, sin evaluación de Conditional Access y sin MFA. El error de un solo usuario privilegiado (enlace mal compartido), un compromiso de credenciales o una acción interna filtra el acceso a una VM de producción mediante una URL copiable y pegable. La identidad del destinatario del enlace no se autentica contra el tenant, por lo que toda la actividad posterior a través de esa sesión es atribuible únicamente a la entrada de log de Bastion y al usuario emisor — lo que dificulta la atribución y elude los controles de autenticación fuerte que normalmente condicionan el acceso a Azure.

Remediación · Azure CLI

az network bastion update --resource-group Production-RG --name Contoso_Bastion \
  --enable-shareable-link false

Reversión: az network bastion update --resource-group Production-RG --name Contoso_Bastion --enable-shareable-link true

Validación

az network bastion show -g Production-RG -n Contoso_Bastion \
  --query "{shareable:enableShareableLink,ipConnect:enableIpConnect,tunneling:enableTunneling}" -o table
# Esperado: shareable=false

Radio de impacto de la corrección · Bajo · solo rompe el flujo de compartir por enlace. Las conexiones estándar de Bastion basadas en navegador siguen funcionando. Considere también deshabilitar enableIpConnect a menos que el flujo requiera específicamente conectarse a direcciones IP fuera de la VNet.

F-04Alto DREAD 16 / 25 · D2 R3 E3 A4 D4

Defender for Cloud — todos los planes de pago están deshabilitados.

Recurso
Pricings a nivel de suscripción en Microsoft.Security/pricings
Control
CIS Azure Foundations v2.1.0 § 2.1.1–2.1.16 · MCSB DS-2, GS-1 · NIST 800-53 SI-4

Evidencia

$ jq '.defender_pricings[] | select(.pricing_tier=="Free") | .name' < scripts/_security_extra.json
"VirtualMachines"            # Defender for Servers — deshabilitado
"SqlServers"                 # Defender for SQL — deshabilitado
"SqlServerVirtualMachines"   # Defender for SQL en máquinas — deshabilitado ← protege la BD del CRM
"StorageAccounts"            # Defender for Storage — deshabilitado
"KeyVaults"                  # Defender for Key Vault — deshabilitado
"Arm"                        # Defender for ARM — deshabilitado
"Dns"                        # Defender for DNS — deshabilitado
…

$ jq '.defender_secure_scores[0] | {current,max,percentage}' < scripts/_security_extra.json
{ "current": 16.8, "max": 36, "percentage": 0.4667 }

Compromiso

No hay detección de amenazas por comportamiento en el SQL Server (patrones de consulta sospechosos, señales de inyección SQL, fuerza bruta en inicios de sesión, lecturas de volumen de datos anómalas), no hay señalización de malware / EDR en las VM (Defender for Servers P2 incluye Defender for Endpoint sin licencia adicional), no hay detección de compromiso en ARM (p. ej., concesiones de roles sospechosas o patrones de creación de recursos desde una IP nueva), y no hay JIT VM Access (que requiere Defender for Servers). Un atacante que opera con credenciales capturadas dentro de esta suscripción no recibe ninguna alerta, no deja ninguna señal con grado de detección y no enfrenta ninguna contención automatizada. El Secure Score del 46,67 % refleja esto.

Remediación · Azure CLI

# Habilitar Defender for SQL en SQL VMs (plan de mayor valor para este entorno)
az security pricing create --name SqlServerVirtualMachines --tier 'Standard'

# Habilitar Defender for Servers Plan 2 (incluye MDE, JIT, monitoreo de integridad de archivos)
az security pricing create --name VirtualMachines --tier 'Standard' --subplan 'P2'

# Habilitar Defender for ARM (alerta sobre actividad sospechosa del plano de gestión)
az security pricing create --name Arm --tier 'Standard'

# Habilitar Defender for DNS (detecta C2 basado en DNS)
az security pricing create --name Dns --tier 'Standard'

# Habilitar Defender for Key Vault (cuando se despliegue el KV — véase F-12)
az security pricing create --name KeyVaults --tier 'Standard'

Costo mensual indicativo: Defender for SQL en una sola VM ≈ US$ 15, Defender for Servers P2 ≈ US$ 15/VM × 6 = US$ 90, Defender for ARM ≈ US$ 4 por millón de operaciones ARM (≈ US$ 5/mes para esta escala), Defender for DNS ≈ US$ 0,70 / 1M de consultas. Total ≈ US$ 115–130 / mes para una línea base de seguridad integral.

Reversión: az security pricing create --name <plan> --tier 'Free'

Validación

az security pricing list \
  --query "value[?contains('VirtualMachines SqlServerVirtualMachines Arm Dns',name)].{name:name,tier:pricingTier}" -o table
# Esperado: todos en 'Standard'

Radio de impacto de la corrección · Bajo · los planes de Defender funcionan como monitoreo de la plataforma sin huella de agente en las VM (Defender for Servers P2 despliega MDE, que se comporta bien en Windows Server 2025).

F-05Alto DREAD 17 / 25 · D3 R4 E2 A5 D3

Sin diagnostic settings en ningún recurso en alcance; el Activity Log no se exporta.

Recursos
Todas las NSG, todas las VM, la VNet, el VPN Gateway, el Recovery Vault, el propio ámbito de la suscripción
Control
CIS Azure v2.1.0 § 5.1.1, § 5.3, § 5.4 · MCSB LT-3, LT-4 · NIST 800-53 AU-2, AU-6, AU-11

Evidencia

$ jq '.sub_activity_log_diag_settings.value | length' < scripts/_security_extra.json
0

$ jq '.resource_diag_settings[] | {r:.resource, count:(.body.value|length)}' < scripts/_security_extra.json
# Los 11 recursos devuelven count=0

Compromiso

La retención del Activity Log es de forma predeterminada 90 días en la suscripción, tras los cuales se purga. Los logs de recursos (aciertos de reglas de NSG, eventos de túnel del VPN gateway, eventos de eliminación / recuperación de vaults, registros de flujo de NIC) no se retienen en absoluto. Un adversario que opera con la lentitud suficiente para dejar pasar la ventana de 90 días no deja ningún rastro recuperable de las acciones del plano de gestión; un adversario que opera de forma ruidosa deja rastros que cualquiera con acceso al Activity Log puede eliminar en sitio (sin copia inmutable). La reconstrucción forense de una brecha está limitada por la telemetría de la plataforma que sobrevive en la ventana de retención predeterminada, y ninguna investigación interna puede mapear "quién cambió la NSG el día X" más allá de 90 días. Esto también impide cumplir cualquier obligación de "retención de logs de auditoría ≥ 1 año" según NIST 800-53 AU-11.

Remediación · Bicep

// 1. Crear un workspace central de Log Analytics
resource law 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
  name: 'contoso-security-law'
  location: 'eastus'
  properties: {
    sku: { name: 'PerGB2018' }
    retentionInDays: 90  // retención en caliente; el largo plazo va a storage
    features: { immediatePurgeDataOn30Days: false }
  }
}

// 2. Activity Log a nivel de suscripción → workspace
resource subDiag 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'send-activity-log'
  scope: subscription()
  properties: {
    workspaceId: law.id
    logs: [
      { category: 'Administrative',  enabled: true }
      { category: 'Security',        enabled: true }
      { category: 'ServiceHealth',   enabled: true }
      { category: 'Alert',           enabled: true }
      { category: 'Recommendation',  enabled: true }
      { category: 'Policy',          enabled: true }
      { category: 'Autoscale',       enabled: true }
      { category: 'ResourceHealth',  enabled: true }
    ]
  }
}

// 3. Aplicar diagnostic settings por recurso — lo más sencillo como una iniciativa de Azure Policy
//    "Configure Azure Monitor for resources" asignada a nivel de suscripción.

Costo indicativo: el pricing de LA PerGB2018 es de US$ 2,76 / GB ingerido (eastus). Para esta escala: aciertos de NSG + Activity Log + métricas de VM ≈ 5–15 GB / mes = US$ 14–41 / mes.

Reversión: az resource delete --ids <diagnosticSettings_id> por recurso. El propio workspace usa soft-delete de 31 días (recuperable).

Validación

az monitor diagnostic-settings list --resource /subscriptions/a1b2c3d4-… -o table
# Debería devolver una entrada (send-activity-log)

Radio de impacto de la corrección · Bajo · los diagnostic settings son observadores pasivos; no pueden afectar el rendimiento de la carga de trabajo.

F-06Alto DREAD 17 / 25 · D3 R4 E2 A5 D3

Cero alertas del Activity Log.

Alcance
Toda la suscripción
Control
CIS Azure v2.1.0 § 5.2.1–5.2.9 · MCSB IR-3 · NIST 800-53 IR-4, SI-4

Evidencia

$ jq '.activity_log_alerts_raw.value | length' < scripts/_security_extra.json
0

Compromiso

No hay detección en tiempo real de las operaciones del plano de control que un atacante usa para ampliar el acceso, suprimir la detección o destruir evidencia: Microsoft.Authorization/roleAssignments/write (escalada de privilegios), Microsoft.Network/networkSecurityGroups/securityRules/delete (desmantelamiento del firewall), Microsoft.RecoveryServices/vaults/delete (destrucción de copias de seguridad), Microsoft.Compute/virtualMachines/delete (VM de producción destruida), Microsoft.KeyVault/vaults/delete (KV destruido), Microsoft.Security/pricings/write (plan de Defender deshabilitado para suprimir alertas). La primera oportunidad del defensor de notar cualquiera de estas es la siguiente visita manual al portal — para entonces la acción ya está completa.

Remediación · Azure CLI

# Crear un action group (email a Dana + opcionalmente SMS / webhook de Teams)
az monitor action-group create -g Production-RG -n SecurityAlerts \
  --short-name SecAlert \
  --email-receivers name=dana email=dana.okoye@contoso.com

AG=$(az monitor action-group show -g Production-RG -n SecurityAlerts --query id -o tsv)
SUB=/subscriptions/a1b2c3d4-1111-4a22-9c33-0d44e55f6a77

# Alerta: escritura de asignación de rol
az monitor activity-log alert create -g Production-RG -n alert-role-assignment-write \
  --scope $SUB --action $AG \
  --condition category=Administrative \
  --condition operationName=Microsoft.Authorization/roleAssignments/write

# Alerta: eliminación de regla de NSG
az monitor activity-log alert create -g Production-RG -n alert-nsg-rule-delete \
  --scope $SUB --action $AG \
  --condition category=Administrative \
  --condition operationName=Microsoft.Network/networkSecurityGroups/securityRules/delete

# Alerta: eliminación de Recovery Services vault
az monitor activity-log alert create -g Production-RG -n alert-vault-delete \
  --scope $SUB --action $AG \
  --condition category=Administrative \
  --condition operationName=Microsoft.RecoveryServices/vaults/delete

# Alerta: cambio de nivel de pricing de Defender (downgrade)
az monitor activity-log alert create -g Production-RG -n alert-defender-pricing-change \
  --scope $SUB --action $AG \
  --condition category=Administrative \
  --condition operationName=Microsoft.Security/pricings/write

# Alerta: eliminación de Key Vault (después de implementar F-12)
# Alerta: eliminación de VM en la capa de producción

Reversión: az monitor activity-log alert delete -g Production-RG -n <name>

Validación

az monitor activity-log alert list -g Production-RG --query "[].{name:name,enabled:enabled}" -o table
# Esperado: ≥ 4 alertas, todas habilitadas

Radio de impacto de la corrección · Bajo · notificación puramente pasiva.

F-07Alto CVSS v3.1 7,5 · AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:H/A:H

Recovery Services vault — inmutabilidad y autorización multiusuario deshabilitadas.

Recurso
/subscriptions/a1b2c3d4-…/resourceGroups/Production-RG/providers/Microsoft.RecoveryServices/vaults/Production-Vault
Control
MCSB DP-9 · NIST 800-53 CP-9, CP-10 · CIS Azure v2.1.0 § 7

Evidencia

$ jq '.production_vault.properties.securitySettings' < scripts/_security_extra.json
{
  "immutabilitySettings":  { "state": null },                                          ← OFF
  "softDeleteSettings":    { "softDeleteState":"Enabled", "softDeleteRetentionPeriodInDays": 14 },
  "multiUserAuthorization": "Disabled"                                                 ← OFF
}

Compromiso

Con la inmutabilidad desactivada y la autorización multiusuario desactivada, un atacante que adquiera Backup Contributor o superior puede acortar la retención, deshabilitar trabajos de copia de seguridad, detener la protección de elementos protegidos y — tras dejar pasar la ventana de soft-delete de 14 días — purgar por completo los puntos de recuperación. El soft-delete defiende contra la eliminación accidental pero no contra un adversario dispuesto a esperar, y el mismo RBAC que destruye también puede acortar la ventana de soft-delete. La recuperación tras una intrusión destructiva exitosa contra las VM de producción no tendría, tras esto, ninguna capa de copia de seguridad a la que recurrir.

Remediación · Azure CLI

# Paso 1 — Habilitar la inmutabilidad primero en modo "Unlocked" (aún reversible)
az backup vault update -g Production-RG -n Production-Vault \
  --immutability-state Unlocked

# Paso 2 — Tras confirmar que todas las copias de seguridad, políticas de retención y el
#         instrumental operativo son correctos durante ≥ 2 semanas, bloquear la inmutabilidad:
# az backup vault update -g Production-RG -n Production-Vault \
#   --immutability-state Locked
# ⚠ Una vez en Locked, la inmutabilidad no puede deshabilitarse. Esto es intencional —
#    es la propiedad que hace que la capa de copia de seguridad sea intocable por ransomware.

# Paso 3 — Habilitar la autorización multiusuario (MUA): requiere un Resource Guard
az resource-guard create -g Production-RG -n contoso-rg-guard --location eastus
GUARD_ID=$(az resource-guard show -g Production-RG -n contoso-rg-guard --query id -o tsv)

az backup vault resource-guard-mapping update -g Production-RG -n Production-Vault \
  --resource-guard-id $GUARD_ID

Reversión: az backup vault update --immutability-state Disabled (solo válido antes de Locked) + az backup vault resource-guard-mapping delete.

Validación

az backup vault show -g Production-RG -n Production-Vault \
  --query "properties.securitySettings.{imm:immutabilitySettings.state, mua:multiUserAuthorization}" -o table
# Esperado: imm=Unlocked (luego Locked tras 2 semanas), mua=Enabled

Radio de impacto de la corrección · Medio · la inmutabilidad Unlocked es reversible; Locked es permanente (por diseño). MUA impone un flujo de aprobación de dos partes sobre las operaciones destructivas de copia de seguridad — capacite a los operadores antes de habilitarla.

F-08Alto CVSS v3.1 7,5 · divulgación de información + amplificación

DNS (TCP + UDP 53) abierto al internet público en los DC y LANSvr.

Recursos
DomainControllers-NSG reglas 1010 / 1020, Servers-NSG reglas 1010 / 1020. VM afectadas: DCSvr-001, DCSvr-002, LANSvr-001.
Control
CIS Azure v2.1.0 § 6.x · MCSB NS-2 · NIST 800-53 SC-7, SC-22

Evidencia

$ jq '.network.nsgs[].rules[] | select(.dstPort=="53")' < scripts/_security_audit.json
[
  { "name":"PermitDNS-TCP","srcAddr":"*","direction":"Inbound","access":"Allow","protocol":"TCP","dstPort":"53" },
  { "name":"PermitDNS-UDP","srcAddr":"*","direction":"Inbound","access":"Allow","protocol":"UDP","dstPort":"53" }
  …  // repetido para Servers-NSG
]

Compromiso

Dos riesgos distintos. (a) Resolver abierto recursivo — AD-DNS responde de forma predeterminada cualquier consulta; los atacantes lo explotan para ataques de reflexión por amplificación de DNS contra terceros, siendo la percepción del tercero sobre el origen del ataque el espacio de IPs de esta suscripción (violación de NIST SC-22, listada en la guía de mitigación de DDoS). (b) Divulgación de zona integrada en AD — los registros de corp.contoso.com (cada servidor, cada estación de trabajo, cada service principal name registrado en AD-DNS) pasan a ser enumerables por cualquiera en internet mediante consultas directas, proporcionando un mapa completo de reconocimiento del patrimonio interno antes de que se intente autenticación alguna. dig @203.0.113.11 corp.contoso.com AXFR puede tener éxito o no según las ACL de transferencia de zona de AD-DNS, pero el sondeo de registros individuales siempre funciona.

Remediación · Azure CLI

# Estos puertos nunca deberían exponerse externamente; AD-DNS debería atender a los clientes vía la VPN
# o vía Private DNS Resolver, no directamente.
for nsg in DomainControllers-NSG Servers-NSG; do
  az network nsg rule delete -g Production-RG --nsg-name $nsg --name PermitDNS-TCP
  az network nsg rule delete -g Production-RG --nsg-name $nsg --name PermitDNS-UDP
done

# Confirmar que las VM internas siguen resolviendo vía los DC (la regla intra-VNet AllowVnetInBound lo permite)

Reversión: vuelva a crear las reglas con las mismas prioridades y srcPrefix=*.

Validación

for nsg in DomainControllers-NSG Servers-NSG; do
  az network nsg rule list -g Production-RG --nsg-name $nsg \
    --query "[?destinationPortRange=='53']" -o table
done
# Esperado: vacío

Radio de impacto de la corrección · Medio · verifique que ningún servidor on-premises apunte a las IPs públicas de los DC para resolución DNS antes de aplicar. La ruta adecuada para on-premises es a través del túnel VPN S2S (actualmente 0 bytes — por lo que es poco probable que esto rompa algo en la práctica, pero confírmelo con Dana).

F-09Alto A nivel de diseño · hallazgo CIS

Los controladores de dominio y el servidor SQL tienen IPs públicas.

Recursos
DCSvr-001-ip, DCSvr-002-ip, DBSvr-001-ip, LANSvr-001-ip
Control
CIS Azure v2.1.0 § 6.6 · MCSB NS-1 · NIST 800-53 SC-7

Evidencia: véase la tabla de § 4 IPs públicas.

Compromiso

La capa de identidad (DC) y la capa de datos (BD) nunca deberían llevar IPs públicas en un entorno de producción. Incluso con la NSG acotada a una única IP de origen, la existencia de la asociación significa que: (1) cualquier error en una regla de NSG expone el recurso de inmediato a internet — un solo "Any" tecleado por error en una edición futura de una regla está a un clic de la exposición total; (2) el recurso es enumerable por escáneres de todo internet (Shodan, Censys) y aparece en sus conjuntos de datos públicos sin importar el estado de la NSG; (3) un proceso que se ejecuta en el host y que se enlaza a su interfaz de cara al público (intencionalmente o por mala configuración) obtiene una ruta de salida que elude cualquier futuro Azure Firewall / NAT Gateway diseñado para centralizar la inspección de egress.

Remediación: véase F-02 Paso 3 — las cuatro IPs públicas se desasocian como parte de esa corrección una vez validados Bastion + VPN-P2S.

Validación

az network nic show -g Production-RG -n dbsvr-001887 \
  --query "ipConfigurations[].publicIPAddress" -o tsv
# Esperado: vacío

Radio de impacto de la corrección · Alto · condicionado a F-02 (Bastion + VPN listos).

F-10Alto (operativo) DREAD 17 / 25 · D2 R3 E5 A4 D3

La autenticación P2S del gateway VPN está sin configurar (bloqueo operativo).

Recurso
/subscriptions/a1b2c3d4-…/resourceGroups/Production-RG/providers/Microsoft.Network/virtualNetworkGateways/Azure-ContosoNet
Control
MCSB IM-1 · NIST 800-53 IA-2(11)

Evidencia

$ jq '.virtual_network_gateways.value[0].properties.vpnClientConfiguration | {protocols:vpnClientProtocols, auth:vpnAuthenticationTypes, rootCerts:(vpnClientRootCertificates|length), aad:aadTenant, radius:radiusServerAddress}' < scripts/_security_extra.json
{
  "protocols": ["OpenVPN","IkeV2"],
  "auth":      [],
  "rootCerts": 0,
  "aad":       null,
  "radius":    null
}

Compromiso

Los trabajadores remotos no pueden usar la VPN — los protocolos están definidos pero no hay ningún método de autenticación configurado, por lo que ningún cliente puede completar un handshake. Esto obliga a usar la ruta RDP directa por IP pública de la VM (F-02), que es la mayor superficie de ataque actual. El despliegue de Bastion mitiga esto parcialmente para sesiones desde dispositivos que ya tienen una sesión del portal de Azure, pero no hace nada por los endpoints no administrados que necesitan acceso directo dentro de la VNet. El compromiso que esto crea es estructural: la organización no puede retirar la superficie RDP de IP pública (F-02 / F-09) sin entregar primero esta ruta de autenticación VPN, por lo que cada día que pasa sin autenticación P2S de Entra ID es otro día en que las seis IPs públicas de las VM deben permanecer asociadas.

Remediación · Terraform

resource "azurerm_virtual_network_gateway" "contosonet" {
  # ... configuración existente ...

  vpn_client_configuration {
    address_space        = ["172.16.201.0/24"]
    vpn_client_protocols = ["OpenVPN"]
    vpn_auth_types       = ["AAD"]

    aad_tenant   = "https://login.microsoftonline.com/e7f8a9b0-2222-4b33-8d44-1e55f6607b88/"
    aad_audience = "c632b3df-fb67-4d84-bdcf-b95ad541b5c8"  # app AAD del cliente VPN de Azure
    aad_issuer   = "https://sts.windows.net/e7f8a9b0-2222-4b33-8d44-1e55f6607b88/"
  }
}

Requisito previo: una política de Conditional Access dirigida a la aplicación VPN de Azure que exija MFA + dispositivo conforme.

Reversión: elimine el bloque vpn_client_configuration.

Validación

az network vnet-gateway show -g Production-RG -n Azure-ContosoNet \
  --query "vpnClientConfiguration.{auth:vpnAuthenticationTypes, aad:aadTenant}" -o table
# Esperado: auth=AAD, aad=https://login.microsoftonline.com/e7f8a9b0-…/

Radio de impacto de la corrección · Medio · habilita una nueva ruta de entrada; verifique que la política de Conditional Access sea correcta antes de anunciar el acceso a los usuarios.

F-11Medio DREAD 14 / 25 · D2 R3 E3 A3 D3

NSG Flow Logs deshabilitados — sin visibilidad de la anomalía de egress.

Recursos
Las 5 NSG (sin flow logs en Network Watcher)
Control
CIS Azure v2.1.0 § 6.5 · MCSB LT-3 · NIST 800-53 AU-12

Evidencia

$ jq '.flow_logs.value | length' < scripts/_security_extra.json
0

Compromiso

La anomalía de egress de red saliente de los DC (910 GB / mes en DC1, 640 GB / mes en DC2 — véase dc-performance-20260514.md) no puede caracterizarse sin flow logs. Podemos ver cuánto está saliendo pero no hacia dónde. Esto bloquea tanto la cuestión de costo (mover el destino de la copia de seguridad a la misma región si corresponde) como la de seguridad — solo con las métricas del host no podemos distinguir "AzureBackup subiendo a un vault" de "un proceso no identificado exfiltrando datos". Los 21–40 GB / día de salida por DC están dos o tres órdenes de magnitud por encima de lo esperado para un controlador de dominio, y sin flow logs las IPs de destino son imposibles de conocer desde fuera del SO invitado.

Remediación · Azure CLI

# Crear una storage account para los flow logs (eastus, LRS Standard)
az storage account create -g Production-RG -n contosoflowlogs$RANDOM \
  --location eastus --sku Standard_LRS --kind StorageV2 \
  --min-tls-version TLS1_2 --allow-blob-public-access false
SA=$(az storage account list -g Production-RG --query "[?starts_with(name,'contosoflowlogs')].id" -o tsv)

for nsg in ACC-001az-nsg ADM-001az-nsg DBServers-NSG DomainControllers-NSG Servers-NSG; do
  NSG_ID=$(az network nsg show -g Production-RG -n $nsg --query id -o tsv)
  az network watcher flow-log create -l eastus -n flowlog-$nsg \
    --nsg $NSG_ID --storage-account $SA --retention 90
done

# Opcionalmente habilitar Traffic Analytics (requiere un workspace de Log Analytics)

Reversión: az network watcher flow-log delete -l eastus -n flowlog-<nsg>

Validación

az network watcher flow-log list -l eastus -o table
# Esperado: 5 entradas, enabled=True

Radio de impacto de la corrección · Bajo · los flow logs son pasivos.

F-12Medio DREAD 14 / 25 · D3 R3 E2 A4 D2

Sin Key Vault desplegado — los secretos y certificados no se gestionan de forma centralizada.

Alcance
Toda la suscripción (existen cero Microsoft.KeyVault/vaults)
Control
CIS Azure v2.1.0 § 8 · MCSB DS-6 · NIST 800-53 SC-12, SC-28

Evidencia

$ jq '.key_vaults | length' < scripts/_security_audit.json
0

Compromiso

No hay un almacén de secretos central, auditado y controlado por RBAC. Todo material secreto actualmente en uso vive donde el operador lo haya colocado — archivos .env en las laptops de los operadores, archivos de configuración en las VM, o inline en la propia definición del recurso. Ejemplos concretos conocidos:

  • El client secret del SP de auditoría vive actualmente en .env en la laptop del consultor (AZURE_CLIENT_SECRET=…).
  • La clave precompartida IPSec para la conexión S2S ContosoNet (sharedKey: false en la respuesta de la API — o bien actualmente nula o almacenada fuera de Azure).
  • La conexión azurevm de Logic App contiene la credencial que requiera su conector V1.
  • Cualquier credencial de aplicación usada por las cargas de trabajo en las VM se almacena en la configuración local que esas cargas de trabajo esperen.

Sin un Key Vault no hay ciclo de vida de rotación, ni telemetría de uso, ni control de acceso acotado sobre estas credenciales, ni un lugar donde poner futuras credenciales (clave TDE de Defender for SQL, CMK para storage o discos una vez que exista Key Vault).

Remediación · Bicep

resource kv 'Microsoft.KeyVault/vaults@2024-04-01-preview' = {
  name: 'contoso-prod-kv-${uniqueString(resourceGroup().id)}'
  location: 'eastus'
  properties: {
    tenantId:                 subscription().tenantId
    sku:                      { family: 'A', name: 'standard' }
    enableRbacAuthorization:  true       // NO access policies
    enableSoftDelete:         true
    softDeleteRetentionInDays: 90
    enablePurgeProtection:    true
    publicNetworkAccess:      'Disabled'
    networkAcls: {
      defaultAction: 'Deny'
      bypass:        'AzureServices'
    }
  }
}
// Además, un Private Endpoint hacia vNet-001 para acceso en runtime desde las VM.

Reversión: az keyvault delete (soft-delete de 90 días recuperable, la purge-protection fuerza la recuperación).

Validación

az keyvault list --query "[].{name:name, rbac:properties.enableRbacAuthorization, pna:properties.publicNetworkAccess, purge:properties.enablePurgeProtection}" -o table
# Esperado: rbac=true, pna=Disabled, purge=true

Radio de impacto de la corrección · Bajo · solo un recurso nuevo.

F-13Medio DREAD 14 / 25 · D4 R2 E2 A4 D2

Sin resource locks en los recursos de producción.

Alcance
Todos los recursos (la consulta a nivel de suscripción devolvió 0 locks)
Control
CIS Azure v2.1.0 § 10 · MCSB GS-6

Evidencia

$ jq '.locks.value | length' < scripts/_security_extra.json
0

Impacto. Una sola acción mal cliqueada en el portal de Azure por cualquiera con Contributor o superior (actualmente: 1 usuario) elimina una VM de producción, la SQL VM o el único par de controladores de dominio. Los resource locks en CanNotDelete impiden que la llamada a la API tenga éxito sin importar el RBAC.

Remediación · Azure CLI

# Bloquear todo el grupo de recursos de producción contra eliminación
az lock create -g Production-RG --name "no-delete-production" --lock-type CanNotDelete \
  --notes "Production environment — break-glass change required to delete"

# Opcionalmente bloquear recursos individuales de alto valor
for r in DBSvr-001 DCSvr-001 DCSvr-002 Production-Vault Azure-ContosoNet Contoso_Bastion; do
  az lock create --resource-group Production-RG --resource-name $r \
    --resource-type <type> --name "no-delete-$r" --lock-type CanNotDelete
done

Reversión: az lock delete --name no-delete-production --resource-group Production-RG

Validación

az lock list -g Production-RG -o table
# Esperado: 1+ entradas de lock

Radio de impacto de la corrección · Bajo · los locks solo bloquean operaciones destructivas; no afectan el runtime.

F-14Medio DREAD 13 / 25 · D3 R3 E2 A3 D2

Dos usuarios tienen Owner de la suscripción; sin PIM / sin distinción break-glass.

Alcance
Asignaciones de roles a nivel de suscripción
Control
CIS Azure v2.1.0 § 1.21, § 1.23 · MCSB PA-1, PA-2 · NIST 800-53 AC-6(5)

Evidencia

IDs de roles decodificados — 8e3af657-… = Owner, b24988ac-… = Contributor, acdd72a7-… = Reader

Owner       (ámbito sub): 7a8b9c0d-7777-4088-9299-633aebb5fa43 (User)
Owner       (ámbito sub): 6f7a8b9c-6666-4f77-8188-5229daa4ef32 (User)   ← también Reader en el mismo ámbito (redundante)
Contributor (ámbito sub): 8b9c0d1e-8888-4199-a3aa-744bfcc60b54 (User)
Reader      (ámbito sub): 9c0d1e2f-9999-42aa-b4bb-855c0dd71c65 (SP — identidad de esta auditoría)

Impacto. Dos Owners permanentes de la suscripción significan dos puntos donde el compromiso de credenciales produce la toma total del entorno. Se desconoce el Conditional Access de esos usuarios (el SP carece de Directory.Read.All — véase F-15). Uno de los dos owners (6f7a8b9c-…) también tiene Reader en el mismo ámbito, lo cual es peso muerto (Owner ya otorga lectura).

Remediación

Requiere Entra ID Premium P2 para PIM. Sin P2:

# Paso 1 — Auditar quiénes son los dos GUID de Owner (requiere GA de Entra ID o un usuario con User.Read.All)
az ad user show --id 7a8b9c0d-7777-4088-9299-633aebb5fa43 -o table
az ad user show --id 6f7a8b9c-6666-4f77-8188-5229daa4ef32 -o table

# Paso 2 — Eliminar el Reader redundante en 6f7a8b9c-…
ASSIGN_ID=$(az role assignment list --assignee 6f7a8b9c-6666-4f77-8188-5229daa4ef32 \
  --role Reader --scope /subscriptions/a1b2c3d4-… --query "[0].id" -o tsv)
az role assignment delete --ids $ASSIGN_ID

# Paso 3 — Confirmar que ambos Owners tengan una cuenta break-glass designada correspondiente
#          y una política de Conditional Access que exija MFA + dispositivo conforme.

Reversión: vuelva a crear la asignación de rol mediante az role assignment create.

Validación

az role assignment list --scope /subscriptions/a1b2c3d4-… --role Owner -o table
# Esperado: 2 entradas (o 1 + break-glass) — no más

Radio de impacto de la corrección · de Bajo (eliminar el Reader redundante) a Alto (modificar las relaciones reales de Owner — coordine con Dana).

F-15Informativo Evidencia requerida

Lectura del directorio de Entra ID denegada a la identidad de auditoría.

Recurso
El propio SP de auditoría (0c1d2e3f-3333-4c44-9e55-2f66a7718c99)

Evidencia

$ jq '.entra' < scripts/_security_audit.json
{
  "service_principals_count_status": 200,
  "directory_roles_status":          403,
  "conditional_access_status":       403,
  "security_defaults_status":        403,
  "conditional_access_error": "AccessDenied: required scopes are missing in the token"
}

Impacto. La auditoría no puede responder 8 de los controles de identidad de CIS § 1.x sin lectura del directorio de Entra ID:

  • § 1.1 — security defaults habilitados
  • § 1.2–1.13 — MFA, autenticación heredada, cobertura de Conditional Access
  • § 1.21–1.22 — restricciones de invitación de invitados

Estos son controles de identidad fundamentales. Sin evidencia, se marcan como Evidence required en lugar de Pass o Fail.

Remediación

  • Otorgue al SP de auditoría Global Reader (recomendado — la lectura más amplia, cero escritura), O
  • Permisos de aplicación Directory.Read.All + Policy.Read.All en Microsoft Graph (requiere consentimiento de administrador).

Validación: vuelva a ejecutar scripts/azure_security_audit.py y confirme entra.conditional_access_status: 200 y una lista directory_roles no vacía.

Radio de impacto de la corrección · Bajo · concesión de permiso de solo lectura.

F-16Medio DREAD 12 / 25 · D2 R2 E3 A3 D2

Las VM carecen de identidades administradas; las credenciales del SP se almacenan en .env.

Recursos
Las 6 VM (identity: None)
Control
MCSB IM-3 · NIST 800-53 IA-5(11) · CIS Azure § 1.20

Evidencia

$ jq '.compute.vms[] | {name, identity}' < scripts/_security_audit.json
{ "name":"DBSvr-001","identity":null }    ← repetido para las 6

$ jq '.network.flow_logs.value | length' < scripts/_security_extra.json
0    ← sin traza de auditoría del uso de secretos en ausencia de Defender for ARM + flow logs

Impacto. Cualquier llamada de una carga de trabajo a la API de Azure desde una VM debe usar (a) un secreto de SP almacenado (el patrón actual — el secreto vive en archivos .env distribuidos por las laptops de los operadores), o (b) una credencial de usuario almacenada. La asignación de una identidad administrada a una VM le da a la carga de trabajo un token emitido por Entra que rota automáticamente, se acota vía RBAC y nunca aparece en ningún archivo. El secreto del SP de auditoría ya ha quedado expuesto a múltiples sesiones de Claude (véase el informe de identidad del CRM, sección "Side fix").

Remediación · Azure CLI

# Habilitar la identidad administrada asignada por el sistema en cada VM
for vm in DBSvr-001 ADM-001az ACC-001az DCSvr-001 DCSvr-002 LANSvr-001; do
  az vm identity assign -g Production-RG -n $vm
done

# Luego migrar las cargas de trabajo para usar la identidad (depende de qué se ejecute en cada VM).
# Rotar el secreto del SP de auditoría en paralelo.

Reversión: az vm identity remove -g Production-RG -n $vm

Validación

az vm list -g Production-RG --query "[].{name:name,identity:identity.type}" -o table
# Esperado: SystemAssigned para cada VM

Radio de impacto de la corrección · Bajo · agregar la identidad no es disruptivo; la migración de la carga de trabajo que la usa se acota por separado.

F-17Bajo DREAD 8 / 25 · D2 R2 E1 A2 D1

Sin DDoS Protection Standard en la VNet que aloja cargas de trabajo de cara al público.

Recurso
vNet-001 · enableDdosProtection: false
Control
CIS Azure v2.1.0 § 6.x · MCSB NS-5 · NIST 800-53 SC-5

Evidencia

$ jq '.network.vnets[0] | {name, ddos: .ddos_protection}' < scripts/_security_audit.json
{ "name":"vNet-001","ddos":false }

Impacto. Azure DDoS Protection Standard proporciona mitigación L3 / L4 para las IPs públicas en la VNet protegida. Sin él, los endpoints de cara al público dependen de la protección DDoS Basic gratuita de Azure, que es un pool compartido. Después de que F-02 + F-09 reduzcan la cantidad de IPs públicas a tres (VPN GW + Bastion), DDoS Standard pasa a ser mucho menos importante.

Remediación · Azure CLI

# Generalmente solo vale la pena desplegarlo una vez reducida la superficie de ataque pública
# Y si una carga de trabajo de cara al público requiere mitigación garantizada.
# Costo: US$ 2.944 / mes fijo sin importar cuántas VNet — sugiere diferir hasta que la necesidad del negocio lo justifique.

Validación: tras la remediación, vNet-001.enableDdosProtection = true.

Radio de impacto de la corrección · Bajo · pero el costo es alto en relación con el tamaño de la flota.

F-18Bajo Higiene de seguridad

La conexión de API azurevm de Logic App está rota (estado Error).

Recurso
/subscriptions/a1b2c3d4-…/resourceGroups/production-rg/providers/Microsoft.Web/connections/azurevm

Evidencia

$ jq '.web_connections[]' < scripts/_security_extra.json
{ "name":"azurevm","kind":"V1","properties":{ "api":{"displayName":"Azure VM","name":"azurevm"},
  "overallStatus":"Error","changedTime":"2026-05-11T23:50:12Z" } }

Impacto. Una conexión de API V1 a "Azure VM" se creó el 2026-05-11 o antes y desde entonces está en estado Error. Las conexiones V1 almacenan credenciales en la capa del conector; incluso cuando están rotas, la referencia a la credencial persiste. Si no se usa, elimínela.

Remediación · Azure CLI

az resource delete --ids /subscriptions/a1b2c3d4-…/resourceGroups/production-rg/providers/Microsoft.Web/connections/azurevm

Validación: az resource list --resource-type Microsoft.Web/connections -g production-rg -o table no devuelve filas.

Radio de impacto de la corrección · Bajo · si hay una Logic App que dependa de ella (ninguna visible actualmente en el inventario), fallará. Verifique primero con Dana.

F-19Bajo / Informativo

Programación de apagado automático de DevTest deshabilitada en ADM-001az.

Recurso
/subscriptions/a1b2c3d4-…/resourceGroups/PRODUCTION-RG/providers/microsoft.devtestlab/schedules/shutdown-computevm-ADM-001AZ

Evidencia: véase el inventario de § 3.

Impacto. Se configuró un apagado automático para ADM-001az y luego se deshabilitó (y se desactivaron las notificaciones). Operativamente correcto — solo informativo. Se anota porque las programaciones con notificaciones deshabilitadas pueden enmascarar costos / apagados inesperados más adelante si se rehabilitan.

Remediación: ninguna requerida. Si queda en desuso permanente: elimínela.

Radio de impacto de la corrección · Bajo

F-20Informativo

El cifrado de disco es gestionado por la plataforma (sin CMK).

Recursos
Los 10 discos administrados (encryption.type: EncryptionAtRestWithPlatformKey)
Control
CIS Azure v2.1.0 § 8.x · MCSB DP-5 · NIST 800-53 SC-12

Evidencia: los bloques de cifrado en todos los discos muestran type: EncryptionAtRestWithPlatformKey. No existen disk encryption sets.

Impacto. Los discos están cifrados con claves gestionadas por Microsoft (predeterminado). Para la mayoría de las cargas de trabajo esto es aceptable — CMK agrega control de rotación y custodia de claves pero genera sobrecarga operativa. Dado que no existe ningún Key Vault (F-12), CMK no es posible actualmente.

Remediación: diferida hasta que se despliegue Key Vault (F-12).

Radio de impacto de la corrección · Medio · el recifrado de disco requiere detener la VM.

§ 6 · Hallazgos positivos — controles que aprueban

Doce cosas que el entorno hace bien.

ControlEstadoEvidencia
Todas las VM usan Generation 2 + Trusted Launch (Secure Boot + vTPM)Aprobadosecurity_profile.securityType: TrustedLaunch, uefiSettings.secureBootEnabled: true, vTpmEnabled: true en las 6 VM
IPs públicas de SKU Standard (no Basic — en desuso desde 2025)AprobadoLas 9 IPs públicas sku: Standard
Soft-delete del recovery vault habilitado con seguridad mejoradaAprobadosoftDeleteState: Enabled, retención 14d, enhancedSecurityState: Enabled
Almacenamiento de copias de seguridad georredundanteAprobadostandardTierStorageRedundancy: GeoRedundant
Sin roles RBAC personalizados con acciones comodínAprobadoCero roles personalizados definidos
Sin deny assignments que eludirAprobadoCero deny assignments
Sin administradores clásicosAprobado (inferido)Las asignaciones de roles son todas roleDefinitions de RBAC
Storage account contosostorageacct1 (RA-GRS, pública, sin uso) eliminadaAprobadoConfirmado vía Resource Graph — microsoft.storage/storageaccounts count = 0 al momento de la auditoría
Sin contenedores de storage accesibles públicamenteN/ASin storage accounts
Network Watcher desplegadoAprobadoNetworkWatcher_eastus existe (flow logs no habilitados — véase F-11)
Gateway VPN activo-activoAprobado (resiliencia)activeActive: True, capacidad 2
Azure Bastion desplegadoAprobado (arquitectónico)Contoso_Bastion SKU Standard, 2 scale units — aunque véase F-03 por el Shareable Link

§ 7 · CIS Azure Foundations v2.1.0 — resumen de brechas de control

Dónde la auditoría pudo concluir, y dónde no.

SecciónAprobadoFallaN/AEvidencia requerida
§ 1 — Identidad y acceso00022 · el SP carece de Directory.Read.All (F-15)
§ 2 — Microsoft Defender for Cloud01400 (F-04)
§ 3 — Storage Accounts— (sin storage accounts)
§ 4 — Servicios de base de datos— (sin Azure SQL DB / MI — SQL en una VM)
§ 5 — Registro y monitoreo0900 (F-05, F-06, F-11)
§ 6 — Redes1500 (F-01, F-02, F-08, F-09, F-11)
§ 7 — Máquinas virtuales2100 (Trusted Launch aprueba, cifrado gestionado por la plataforma F-20)
§ 8 — Key Vault— (sin Key Vault — F-12)
§ 9 — App Service
§ 10 — Gobernanza y locks0200 (F-13, F-14)

§ 8 · Secuenciación recomendada

Seis fases. Detección antes de la reducción de superficie pública.

Cada fase está condicionada por lo que hace segura a la siguiente.

Fase 0 · Esta semana Riesgo · Bajo

Hoy. Sin impacto en el negocio.

F-03 (deshabilitar el shareable link de Bastion) · F-15 (otorgar Global Reader al SP de auditoría)

2 ítems
Fase 1 · Semana 2 Riesgo · Bajo

Detección. Observadores pasivos + costo.

F-04 (Defender for Servers + SQL + ARM + DNS) · F-05 (workspace de LA + diagnostic settings) · F-06 (4 alertas del Activity Log) · F-11 (NSG flow logs)

4 ítems
Fase 2 · Semana 3 Riesgo · Medio

Fortalecimiento de copias de seguridad. Cambio operativo.

F-07 (inmutabilidad del vault Unlocked → Locked tras 14 d; MUA habilitado)

1 ítem
Fase 3 · Semanas 4–5 Riesgo · Medio

Identidad y ruta de acceso.

F-10 (autenticación P2S de VPN con Entra ID) · F-14 (revisión de Owners + eliminar el Reader redundante) · F-16 (identidades administradas)

3 ítems
Fase 4 · Semana 6+ Riesgo · Alto

Reducción de superficie pública. Condicionada a la Fase 3.

F-01 (cerrar SQL 1433 a internet) · F-02 (cerrar todo el RDP de las VM) · F-09 (retirar 4 IPs públicas) · F-08 (cerrar DNS a internet)

4 ítems
Fase 5 · Semana 7 Riesgo · Bajo

Gobernanza.

F-12 (Key Vault) · F-13 (resource locks) · F-18 (eliminar el conector roto) · F-20 (CMK cuando KV esté listo)

4 ítems

§ 9 · Lo que esta auditoría no puede indicarle

Se requiere una ampliación del servicio.

Estos ítems requieren un RBAC que el SP de auditoría no tiene:

Para cerrar estas brechas, otorgue al SP de auditoría Global Reader (Entra) y Reader a nivel de management group (ya cubierto).

Uno crítico, nueve altos. La Fase 0 se entrega esta semana.

Deshabilite el shareable link de Bastion, otorgue Global Reader al SP de auditoría, y le devolvemos un plan limpio de la capa de detección de la Fase 1 en siete días. La reducción de superficie pública de la Fase 4 está condicionada al trabajo de VPN P2S en la Fase 3 — la secuencia importa.

Hable con un ingeniero de seguridad →