1Por qué importa
En f01-superficie-confianza dibujaste dónde están las fronteras. La
consecuencia incómoda de ese dibujo es que, tarde o temprano, alguna se va a cruzar.
No porque el equipo sea malo: porque una frontera es un trozo de código o de
configuración y todo el código tiene defectos. La pregunta útil deja de ser “¿se
puede cruzar?” y pasa a ser “¿qué pasa el minuto después de que se cruce?”.
Esa pregunta es exactamente la que separa un incidente de una anécdota. Una inyección de comandos en un proceso que corre como root, sin restricciones y con credenciales de despliegue en el entorno, es una pérdida total. La misma inyección en un proceso sin capacidades, con el sistema de archivos en solo lectura, sin credenciales y que solo alcanza a un servicio, es un ticket. El exploit es idéntico. Lo que cambia es todo lo que había detrás.
Los tres principios de esta lección son la respuesta a ese minuto después. Defensa en profundidad asegura que quede algo detrás. Mínimo privilegio reduce lo que ese “algo” concede si también cae. Fallo seguro impide que la forma más barata de saltarse un control sea provocarle un error. Y hay un cuarto que los sostiene: secure defaults, que es lo que decide el resultado cuando nadie configuró nada, que es el estado real de la mayoría de los sistemas.
Estás aprendiendo a hacer que el trabajo del atacante no escale. Un sistema bien construido no es el que nadie puede tocar: es aquel en el que el esfuerzo de cada paso siguiente es igual o mayor que el del primero. Cuando el segundo paso es gratis, no tienes capas, tienes una capa con adornos.
2Conceptos
- Defensa en profundidad: varios controles independientes en serie, de modo que la caída de uno no implique la de los demás.
- Mínimo privilegio: cada identidad tiene exactamente los permisos que necesita, sobre exactamente los recursos que necesita, durante exactamente el tiempo que los necesita.
- Secure defaults: la configuración de fábrica es la restrictiva. Abrir requiere una acción deliberada; cerrar no requiere ninguna.
- Fallo seguro: cuando algo va mal —error, tiempo agotado, dato ausente— el resultado por defecto es denegar.
- Security by design: las tres anteriores se deciden mientras se diseña, porque después solo se pueden añadir por encima y eso siempre sale peor y más caro.
3Explicación profunda
La frase “defensa en profundidad” se usa casi siempre mal. Poner dos comprobaciones del mismo tipo no es profundidad: es redundancia. La prueba que distingue una cosa de la otra cabe en una pregunta: ¿existe una causa única capaz de tumbar las dos a la vez? Si la respuesta es sí, tienes una capa contada dos veces.
Los ejemplos son incómodos porque son frecuentes. Autenticación en el proxy y autenticación en la aplicación, ambas resolviendo el token contra el mismo servicio de identidad: si ese servicio se cae o se compromete, caen las dos. Cifrado en tránsito y cifrado en reposo gestionados por el mismo sistema de claves: una clave filtrada anula ambos. Un cortafuegos de red y una lista de direcciones permitidas en la aplicación, las dos alimentadas por el mismo encabezado reenviado que ya viste que puede falsear el cliente. En los tres casos, el segundo control aporta menos de lo que su presencia sugiere, y la sugerencia es la parte peligrosa: el equipo se relaja.
Las capas que sí funcionan tienden a ser de naturaleza distinta: una de red, una de identidad, una de datos, una de ejecución. En el laboratorio, que la red interna no tenga salida y que un contenedor no tenga capacidades del kernel son controles que no comparten ni implementación ni administrador ni modo de fallo. Ahí hay profundidad de verdad.
El mínimo privilegio se enseña como un eje y son tres. El primero es la identidad: quién eres. El segundo es el alcance: sobre qué objetos, y con qué verbos. El tercero, el que casi todo el mundo olvida, es el tiempo: durante cuánto. Un permiso de administrador concedido “para el despliegue del jueves” y nunca retirado es el defecto más común de cualquier auditoría de accesos, y no se detecta mirando la lista de permisos —donde todo parece justificado— sino mirando la fecha en que se concedieron.
Aplicado a procesos, el mínimo privilegio tiene una versión muy concreta y muy
medible en contenedores. Un contenedor por defecto no es una caja sellada: arranca
como root dentro de su namespace, con un conjunto de capacidades del
kernel que le permiten cambiar propietarios de ficheros, abrir sockets en crudo y
algunas cosas más, y con el sistema de archivos escribible. Cada una de esas
concesiones es un permiso que alguien te dio sin que lo pidieras. La forma correcta
de decidir cuáles necesitas es empírica: quítalas todas y devuelve solo las que la
aplicación reclame al romperse.
El fallo seguro es el principio que más dinero cuesta cuando se ignora, porque
el camino de error es el camino menos probado de cualquier sistema. Tiene tres
patrones típicos. El primero: una excepción capturada de forma amplia cuyo
catch devuelve permiso concedido para “no romper la experiencia”. El
segundo: un valor por defecto permisivo cuando un dato falta —el clásico
rol = usuario.rol o "admin" escrito al revés por prisa—. El tercero, el
más sutil: una comprobación que solo se aplica a lo que coincide con un patrón, de
modo que todo lo que no coincide pasa sin revisar. Los tres tienen la misma forma:
la ausencia de una respuesta se interpreta como un sí.
Aquí toca el contraejemplo honesto, porque “fallo seguro siempre” es falso. Una
puerta de cámara acorazada debe quedarse cerrada si se va la luz; una puerta de
emergencia debe abrirse. Cuando lo que está en juego es la vida o la continuidad de
algo crítico, fallar abierto puede ser la decisión correcta. Lo que no es aceptable
es que esa decisión se tome sola, por descuido, dentro de un catch. La
regla es: cerrado por defecto; abierto solo donde alguien lo escribió, lo firmó y
lo puso a producir una alerta.
4Ejemplo sencillo
# Un banco. Cuatro capas, y ninguna comparte modo de fallo. capa 1 puerta con horario control de acceso fisico capa 2 camara y grabacion deteccion, no prevencion capa 3 camara acorazada con clave control de acceso al activo capa 4 apertura con retardo gana tiempo aunque caigan 1, 2 y 3 # Minimo privilegio, en el mismo banco cajero abre su caja, no la camara alcance director abre la camara, en horario de oficina alcance + tiempo tecnico entra acompanado, un dia concreto identidad + tiempo # Fallo seguro y su contraejemplo, puerta contra puerta se va la luz -> camara acorazada se queda CERRADA fallo seguro se va la luz -> puerta de incendios se ABRE fallo abierto, y es lo correcto
Fíjate en la capa 2. Una cámara no impide ningún robo; solo lo registra. Y sin embargo es una capa legítima, porque cambia el cálculo del atacante y porque es la única que sigue funcionando cuando las otras ya han fallado. Las capas de detección cuentan, siempre que alguien mire lo que graban.
5Ejemplo técnico
El servicio app del lab-00-base es un nginx que sirve un
fichero estático montado en solo lectura. Es lo más inofensivo que hay en el
laboratorio, y aun así arranca con más privilegio del que necesita. Mide primero,
antes de opinar:
Tres hechos: el proceso principal es root, tiene un conjunto de capacidades del kernel que nadie pidió, y puede escribir en cualquier parte de su sistema de archivos. Ninguno de los tres hace falta para servir un HTML. Cada uno es un multiplicador del impacto de cualquier fallo futuro en esa imagen.
| Concesión por defecto | Para qué se usa aquí | Qué habilita si algo falla |
|---|---|---|
| Proceso principal como root | Solo para abrir el puerto 80 y cambiar a otro usuario | Modificar cualquier fichero de la imagen, incluido el binario |
| Conjunto de capacidades por defecto | Cambiar propietario de las cachés y ceder privilegios | Sockets en crudo, cambio de propietarios, escaneos desde dentro |
| Sistema de archivos escribible | Nada: el contenido va montado en solo lectura | Persistir un payload que sobreviva a un reinicio del proceso |
Escalada por setuid permitida | Nada | Un binario con el bit activo convierte una ejecución cualquiera en root |
Ahora la otra mitad, el fallo seguro, en el fichero conf/proxy.conf. La
directiva location / reenvía absolutamente todo a la aplicación. Es un
default permisivo: la regla de fábrica dice “sí” y para decir “no” hay que
escribir algo. La versión con secure defaults invierte la carga:
# permisivo por defecto: todo pasa, y lo que no quieras hay que ir negandolo location / { proxy_pass http://app/; } # restrictivo por defecto: nada pasa salvo lo declarado location / { return 404; # nota: 404, no 403 } location = / { proxy_pass http://app/; } location ^~ /estatico/ { proxy_pass http://app/estatico/; }
El detalle del 404 frente al 403 importa y es el mismo que
aparecía en el registro de riesgos de la primera lección: un 403
confirma que el recurso existe. Denegar por defecto y además no confirmar la
existencia son dos decisiones distintas, y las dos son gratis en el momento de
escribir la configuración.
6Diagrama
Ver el mismo diagrama como código Mermaid (editable)
flowchart TD
AT[ataque] --> C1[1 red interna sin salida]
C1 --> C2[2 lista de origen por encabezado]
C2 --> C3[3 autorizacion por objeto]
C3 --> C4[4 sin capacidades solo lectura]
CC{{causa comun: encabezado reenviado}} --> C1
CC --> C2
7Código
Primero el fallo abierto, en su forma más común y más inocente. Este código se escribe con buena intención, pasa la revisión y funciona perfectamente durante meses, hasta el día en que el servicio de permisos tarda de más:
# MAL: el camino de error concede el acceso def puede_ver_factura(usuario, factura_id): try: permisos = servicio_permisos.consultar(usuario) except Exception: # captura amplia: tapa hasta los fallos ajenos return True # "para no romper la experiencia" return factura_id in permisos.facturas
La versión con fallo seguro cambia tres cosas, y ninguna es el return:
# BIEN: deniega, distingue el porque, y hace ruido def puede_ver_factura(usuario, factura_id): try: permisos = servicio_permisos.consultar(usuario, timeout=2) except TiempoAgotado: # 1. excepcion concreta, no Exception metricas.incr("autz.indisponible") # 2. un fallo silencioso no es un fallo seguro raise ServicioNoDisponible() # 3. 503, no 200 con contenido vacio return factura_id in permisos.facturas
El punto 3 es el que más discusión genera. Devolver una lista vacía en vez de un error “deniega”, sí, pero deniega en silencio y con aspecto de respuesta correcta: el usuario ve una pantalla sin facturas y llama a soporte, y nadie relaciona eso con el servicio de permisos. Denegar sin decir que has denegado es una avería, no un control.
Y ahora el mínimo privilegio, en el único formato que aquí se puede comprobar
ejecutando. Este fragmento sustituye al servicio app del laboratorio:
# docker-compose.yml — servicio app con el privilegio recortado app: image: nginx:1.27-alpine volumes: - ./app:/usr/share/nginx/html:ro networks: - lab_interna read_only: true # nada persiste dentro del contenedor tmpfs: # salvo lo que nginx necesita en memoria - /var/cache/nginx - /var/run - /tmp cap_drop: - ALL # se quitan TODAS y se devuelven las que rompan cap_add: - CHOWN - SETUID - SETGID - NET_BIND_SERVICE # abrir el 80, que es puerto privilegiado security_opt: - no-new-privileges:true # ningun setuid podra escalar desde aqui restart: unless-stopped
Ese bloque no es una receta para copiar sin pensar: la lista de cap_add
depende de la imagen. Lo que sí se puede copiar es el método: empezar por
ALL en cap_drop, arrancar, leer el error, devolver una
capacidad, repetir. Cinco minutos de bucle y tienes un privilegio justificado línea
por línea, que es algo que casi ningún despliegue puede enseñar.
8Qué puede salir mal
Capas con causa común. Cuatro controles que dependen del mismo servicio de identidad, del mismo encabezado o de la misma clave son un control con cuatro nombres. Antes de contar capas, escribe al lado de cada una de qué depende.
Mínimo privilegio sin eje temporal. Los permisos se conceden con justificación y se retiran sin ninguna, así que nadie los retira. Un acceso elevado sin fecha de caducidad es un acceso permanente disfrazado de excepción.
El fallo abierto por disponibilidad. “Si el servicio de permisos no
responde, dejamos pasar para no cortar el negocio” es una decisión legítima
si está escrita, tiene dueño y genera una alerta. Escondida en un
catch, es una puerta abierta que además se puede provocar: basta
con hacer que ese servicio tarde.
Usar la profundidad como excusa. “No pasa nada porque hay otra capa detrás” es la frase con la que se cierran hallazgos que deberían arreglarse. La profundidad existe para el fallo que no conoces; gastarla en el que sí conoces es quedarse sin margen justo cuando hace falta.
9Cómo lo abusaría un atacante
Quien ataca no intenta romper cuatro capas: busca la causa común que derriba varias con una sola acción. Por eso los objetivos preferidos son los sistemas transversales —el proveedor de identidad, el gestor de secretos, el pipeline, el agente que corre en todas las máquinas—. Un control que está en todas partes es también un fallo que está en todas partes.
La segunda táctica es forzar el camino de error en vez de superar la comprobación. Si autorizar requiere hablar con un servicio, agotarlo o hacerlo lento es más barato que falsificar un token. La pregunta que se hace un atacante ante cualquier control es literalmente “¿qué pasa si esto no responde?”, y demasiadas veces la respuesta la escribió alguien con prisa a las siete de la tarde.
Y la tercera: usar el privilegio sobrante para hacer permanente un acceso temporal. Una ejecución de comandos dentro de un contenedor efímero se pierde en el siguiente despliegue… salvo que el sistema de archivos sea escribible, en cuyo caso se deja algo escrito; o salvo que haya capacidades de sobra, en cuyo caso se pasa al host o a la red. Cada privilegio que no quitaste es un método de persistencia que no tuvo que inventar.
10Cómo defenderlo
- Escribe al lado de cada control de qué depende. Dos controles con la misma dependencia cuentan como uno solo.
- Recorta el privilegio por método, no por copia: quita todo, arranca, lee el error, devuelve lo mínimo. Deja el bucle documentado en el compose.
- Pon fecha de caducidad a todo permiso elevado. Si el sistema no soporta caducidad, el recordatorio de retirada es parte del ticket de concesión.
- Audita los
catchque rodean decisiones de seguridad. Excepción concreta, denegación, métrica y error visible: los cuatro, o no es fallo seguro. - Haz que el valor por defecto sea el restrictivo, incluyendo el de configuración ausente. Un sistema sin configurar debe ser inútil, no permisivo.
- Prueba el camino de error en los tests: un test que simule el servicio de permisos caído y exija un 503 vale más que tres tests del camino feliz.
11Laboratorio
El mismo laboratorio de siempre. Aquí se usa al revés que en las lecciones anteriores: en vez de enumerar lo que hay, vas a quitar cosas hasta que algo se queje.
cd cursos/seguridad/labs/lab-00-base
docker compose up -d
docker compose exec atacante sh -c "id; grep CapEff /proc/1/status"
Ese es el privilegio de partida de un contenedor cualquiera. Comprueba qué habilita en la práctica, sin cambiar nada todavía:
Ahora recorta. Edita el servicio app del compose con el bloque
de la sección de código, levanta de nuevo y comprueba que la aplicación
sigue sirviendo exactamente igual con el privilegio recortado:
docker compose up -d --force-recreate app
docker compose exec app sh -c "touch /prueba || echo solo lectura, correcto"
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/
Si el último comando devuelve 200, acabas de quitar cuatro
privilegios sin coste funcional ninguno. Cuando termines, revierte el
compose y destruye el laboratorio:
docker compose down -v
12Ejercicios
¿Cuántas capas hay de verdad?
Un sistema declara cinco controles: (1) cortafuegos que solo permite el 443; (2) proxy que valida el token contra el proveedor de identidad; (3) la aplicación que vuelve a validar el mismo token contra el mismo proveedor; (4) cifrado en tránsito con certificados emitidos por la autoridad interna; (5) contenedores sin capacidades y en solo lectura. Di cuántas capas independientes hay realmente y qué causa común agrupa a las que no lo son.
Tres capas independientes: red (1), identidad (2 y 3 juntas) y ejecución (5). El control (4) protege una propiedad distinta —confidencialidad en tránsito— y no aporta profundidad frente a un atacante autenticado, aunque sí frente a uno que escucha la red. Los controles (2) y (3) comparten proveedor de identidad: son una sola capa mirada dos veces.
Clave: 3, no 5. Si tu respuesta fue 5, el error no es de cuenta: es que estabas contando cajas en vez de dependencias.
Recorta el privilegio hasta que se rompa
Aplica al servicio app del lab-00-base el bloque de
cap_drop, read_only y tmpfs de la lección,
pero sin ningún cap_add. Levanta, mira los logs, y ve
devolviendo capacidades de una en una hasta que sirva el HTML. Documenta la lista
final con una línea por capacidad explicando qué se rompía sin ella. Después
revierte el compose.
read_only: true nginx falla antes de arrancar si no le das memoria temporal donde escribir; los logs dicen exactamente qué ruta no pudo abrir.cap_add: [NET_BIND_SERVICE], levanta y lee el error siguiente. El bucle es leer-añadir-repetir, no adivinar la lista entera.
Con la imagen oficial de nginx sirviendo en el puerto 80, la lista mínima
habitual es NET_BIND_SERVICE para abrir el puerto privilegiado,
SETUID y SETGID para que el proceso principal ceda
privilegios a los de trabajo, y CHOWN para ajustar la propiedad
de los directorios temporales al arrancar. Todo lo demás sobra.
El resultado interesante no es la lista: es que existe una alternativa que
hace innecesarias tres de las cuatro. Si la imagen sirviera en un puerto por
encima del 1024 y arrancara ya como usuario sin privilegios, bastaría
cap_drop: [ALL] a secas. Recortar capacidades es bueno; no
necesitarlas es mejor, y esa decisión se toma al elegir la imagen.
Clave: el entregable es la tabla capacidad → qué se rompía sin ella. Una lista copiada de internet sin haber visto el error correspondiente no vale: es exactamente el hábito que esta lección intenta romper.
Qué añade exactamente no-new-privileges
Investiga fuera de esta lección: ¿qué hace la marca del kernel que activa
no-new-privileges, y en qué se diferencia de haber quitado todas las
capacidades? Explica con un ejemplo concreto un caso en el que un contenedor
sin capacidades pueda aun así ganar privilegio, y por qué esa marca lo
impide. Termina diciendo si esa marca sustituye o complementa a
cap_drop: [ALL], y por qué.
docker compose exec app sh -c "find / -perm -4000 -type f 2>/dev/null".La marca hace que ninguna ejecución posterior pueda otorgar más privilegio del que ya tiene el proceso: los bits que normalmente elevan al ejecutar un binario dejan de tener efecto, para ese proceso y para todos sus hijos, de forma irreversible. Es distinto de quitar capacidades: sin capacidades, el proceso no puede hacer ciertas cosas, pero si consigue ejecutar un binario con el bit de elevación activo, el nuevo proceso arranca con el privilegio del propietario del fichero, típicamente root, y recupera lo que le quitaste.
Por eso complementa y no sustituye. cap_drop: [ALL] reduce el
privilegio actual; no-new-privileges impide recuperarlo. Van
juntas, y ninguna de las dos evita que la imagen traiga binarios con ese bit
que no debería traer: quitarlos de la imagen es la tercera acción, y la que
de verdad reduce superficie.
Clave: la respuesta correcta distingue “no puede hacerlo” de “no puede volver a obtenerlo”. Si tu explicación no incluye un mecanismo concreto de recuperación de privilegio, aún no has tocado el punto.
Red Team · demuestra qué capa te está frenando
Dentro del lab-00-base, y solo dentro de él: ejecuta desde
atacante un escaneo a medio abrir contra db y anota que
funciona. Ahora arranca ese contenedor sin la capacidad que ese tipo de escaneo
necesita y repítelo. Demuestra con comandos que (a) el escaneo a medio abrir ya
no funciona y (b) el escaneo por conexión completa sí, y explica qué te dice eso
sobre el valor real de esa capa.
cap_drop en el servicio atacante.-sT, y no necesita ningún privilegio especial porque solo abre conexiones como cualquier programa.
Con cap_drop: [NET_RAW] en el servicio atacante, el
escaneo a medio abrir falla y nmap avisa de que necesita
privilegios; el escaneo con -sT devuelve exactamente el mismo
resultado, porque solo abre conexiones TCP normales. También deja de
funcionar ping, por la misma razón.
Lo que eso dice del control es lo importante: quitar esa capacidad no impide descubrir la base de datos, solo obliga a usar una técnica más ruidosa y más lenta. Es una capa legítima —encarece y hace visible— pero no es una frontera. Confundir “le he quitado una herramienta” con “le he cerrado el paso” es la forma más común de sobrevalorar un hardening.
Clave: revierte el cap_drop del atacante al terminar. El entregable son las dos salidas de nmap, la que falla y la que no, más la frase que explica por qué esto no es una frontera.
Blue Team · audita el camino de error del proxy
Para el servicio app con el laboratorio levantado y pide una página
a través del proxy. Observa qué devuelve y qué queda registrado. Responde: ¿el
comportamiento del proxy cuando su destino no responde es fallo seguro o fallo
abierto? ¿Y el del endpoint /salud, que tiene el registro
desactivado? Propón el cambio mínimo para que ambos casos dejen rastro
suficiente para investigar.
docker compose stop app y luego una petición normal al proxy. Mira el código de estado y el registro de errores por separado del de accesos./salud refleje el estado de la dependencia, y que deje de tener el registro apagado o al menos registre los fallos.
Con app parado, el proxy responde 502 y deja una
línea en el registro de errores: eso es fallo seguro, porque el error no
sirve contenido ni salta ninguna comprobación. El /salud es el
caso interesante: sigue respondiendo 200 proxy ok con la
aplicación caída, y con access_log off no queda constancia de
quién lo consultó ni cuántas veces. No es un fallo abierto de autorización,
pero sí un punto ciego: un atacante puede sondearlo indefinidamente sin
aparecer en ningún sitio.
Cambio mínimo: que el endpoint de salud consulte de verdad a su dependencia antes de responder, y que el registro se apague solo para las respuestas correctas, nunca para los errores. Un punto ciego se justifica por volumen; un punto ciego total no se justifica nunca.
Clave: el entregable son las dos clasificaciones —502 seguro, /salud ciego— y la propuesta concreta. Arranca app otra vez al terminar.
Architecture Challenge · el sistema de autorización que nunca falla abierto
Diseña la autorización de un servicio que consulta permisos a un sistema externo. Enumera todos sus modos de fallo —tiempo agotado, respuesta malformada, caché expirada, permiso desconocido, endpoint nuevo que nadie protegió— y decide para cada uno qué se devuelve, qué se registra y qué se alerta. Define además la única excepción en la que aceptarías fallar abierto, con su mecanismo de aprobación y de caducidad.
Una propuesta razonable: denegación por omisión en el enrutador, de forma que un endpoint sin declaración explícita de permiso devuelva error de configuración en el arranque, no en tiempo de petición —así el fallo aparece en el despliegue y no en producción—. Tiempo agotado y respuesta malformada: denegar con 503, métrica y alerta por tasa, nunca por evento suelto. Permiso desconocido: denegar con 404 hacia fuera y un registro detallado hacia dentro. Caché: permitida con una ventana corta y explícita, con la regla de que una decisión servida desde caché expirada se cuenta aparte.
La excepción para fallar abierto, si existe, se limita a un conjunto de operaciones de solo lectura sin datos sensibles, se activa con una marca que caduca sola en horas, requiere dos personas y genera un aviso continuo mientras está activa. Lo que la hace aceptable no es la lógica: es que sea visible y que se apague sin que nadie tenga que acordarse.
Clave: no hay una única respuesta. Se evalúa que hayas incluido el modo de fallo “endpoint sin proteger”, que separes respuesta de registro, y que la excepción tenga caducidad automática en vez de un recordatorio en el calendario de alguien.
13Preguntas de comprensión
Pones autenticación en el proxy y también en la aplicación. ¿Es defensa en profundidad?
Si ambas resuelven el mismo token contra el mismo proveedor de identidad, son una sola capa mirada dos veces.
Una función de autorización captura la excepción del servicio de permisos y devuelve “permitido”. ¿Cómo se llama eso?
Y además es provocable: agotar o ralentizar ese servicio sale más barato que falsificar un token.
¿Qué hace cap_drop: [ALL] en un servicio de un compose?
Cambiar de usuario es user: y el sistema de archivos es read_only:. Son tres controles distintos y se ponen los tres.
¿Cuándo es aceptable que un control falle abierto?
La puerta de emergencia se abre al irse la luz y eso está bien. Lo que no vale es que lo decida un catch a las siete de la tarde.
14Reto adicional
Presupuesto de capas: elige dos de cinco
Tienes una aplicación web con base de datos y tiempo para implementar dos controles este trimestre. Los candidatos: (1) autenticación multifactor para el panel de administración; (2) contenedores sin capacidades y en solo lectura; (3) segmentación de red entre aplicación y base de datos; (4) registro de auditoría con identidad y correlación; (5) revisión obligatoria de dos personas para cambios en producción. Elige dos, justifica por independencia y por lo que cubren, y di explícitamente qué escenario queda sin cubrir con tu elección.
Una elección defendible: (1) y (4). El multifactor ataca el escenario más probable de acceso inicial —credenciales robadas o reutilizadas— y el registro de auditoría es la única de las cinco que sigue sirviendo cuando cualquiera de las otras falla, porque no previene: cuenta. Son independientes en implementación, en dueño y en modo de fallo.
Lo que queda descubierto, y hay que decirlo: el movimiento lateral hacia la base de datos una vez dentro de la aplicación, que solo cubre (3), y la persistencia dentro del contenedor, que solo cubre (2). Una respuesta distinta —(2) y (3), por ejemplo— también es defendible si el modelo de amenaza pone el acceso inicial como inevitable; lo que no es defendible es elegir sin nombrar el hueco.
Clave: no se evalúa qué dos elegiste, sino que las dos sean independientes y que hayas escrito el escenario que dejas descubierto. Una elección sin hueco declarado es una elección que no se ha pensado.
15Resumen
- Defensa en profundidad no es apilar controles: es que no exista una causa única capaz de tumbar varios a la vez.
- Las capas que funcionan son de naturaleza distinta: red, identidad, datos, ejecución. Las que se repiten comparten modo de fallo.
- Mínimo privilegio tiene tres ejes: identidad, alcance y tiempo. El tercero es el que nadie audita y el que siempre está roto.
- El privilegio de un proceso se mide, no se supone: usuario, capacidades y escritura son tres comandos.
- Se recorta por método: quitar todo, arrancar, leer el error, devolver lo mínimo. Una lista copiada no está justificada.
- Fallo seguro significa que la ausencia de respuesta se interpreta como no. El camino de error es el menos probado y por eso es el más barato de forzar.
- Fallar abierto es a veces correcto, pero solo si está escrito, tiene dueño, hace ruido y caduca solo.
16Mis notas
Estas notas se guardan solo en este navegador. No se envían a ningún servidor. Expórtalas desde el panel de progreso si cambias de máquina.
17Recursos
Los comentarios se cargan solo si los pides, desde GitHub Discussions. No se carga nada de terceros mientras no pulses.