1Por qué importa
En un equipo de producto, la palabra “seguridad” aparece normalmente al final de un sprint, en forma de lista de hallazgos que alguien tiene que priorizar. Esa lista suele mezclar tres cosas distintas: defectos concretos del código, escenarios imaginados de ataque y sensaciones. Si el vocabulario está borroso, la priorización acaba siendo una negociación entre quién habla más fuerte y quién tiene más miedo.
Un vocabulario preciso hace tres cosas muy prácticas. Primero, convierte una discusión en una cuenta: si un riesgo se escribe con actor, vía, activo y consecuencia, se puede estimar. Segundo, permite decir que no: muchos hallazgos con puntuación alta no son prioritarios en tu contexto, y poder demostrarlo evita gastar semanas en lo que no importa. Tercero, hace que las defensas se puedan verificar: si sabes exactamente qué propiedad estás protegiendo, sabes qué prueba tiene que fallar cuando la protección funcione.
No estás aprendiendo definiciones para un examen. Estás aprendiendo a escribir una frase que un ingeniero pueda convertir en un ticket y un directivo pueda convertir en una decisión de presupuesto. Ese es el entregable de esta lección.
2Conceptos
- Confidencialidad: solo quien debe ver el dato lo ve.
- Integridad: el dato es el que debe ser, y se puede demostrar que no cambió por el camino.
- Disponibilidad: el sistema responde cuando se le necesita.
- Amenaza: alguien o algo con capacidad e intención de causar daño.
- Vulnerabilidad: un defecto o una decisión de diseño que ese alguien puede aprovechar.
- Exploit: la técnica concreta que convierte la vulnerabilidad en efecto.
- Impacto: lo que se pierde si el efecto ocurre.
- Riesgo: la combinación de probabilidad e impacto, siempre relativa a un contexto.
3Explicación profunda
La tríada CIA no es una clasificación académica: es una lista de las tres formas en que un sistema puede fallar frente a alguien que actúa a propósito. La utilidad práctica está en que las defensas de una propiedad no defienden las otras, y confundirlas lleva a controles que dan una falsa sensación de cobertura.
Cifrar una base de datos en reposo protege confidencialidad frente a alguien que se lleva el disco. No protege integridad: si la aplicación tiene una inyección SQL, escribe datos falsos a través del cifrado, que se aplica y se retira de forma transparente. Tampoco protege disponibilidad: si pierdes la clave, has construido tú mismo un ataque de denegación de servicio perfecto. Tres propiedades, tres controles distintos.
La segunda familia de términos describe una cadena causal, y el error habitual es saltarse eslabones. Una amenaza sin vulnerabilidad no llega a nada: un atacante muy capaz frente a un sistema sin defecto explotable no produce un incidente. Una vulnerabilidad sin amenaza tampoco: un defecto en un binario que nadie ejecuta y al que nadie llega es deuda técnica, no un incidente en espera. El exploit es lo que une ambos extremos, y su existencia pública cambia radicalmente la probabilidad.
El riesgo es la única de las seis palabras que no describe una propiedad del sistema sino una estimación tuya. Por eso el mismo defecto tiene riesgos distintos en dos empresas, y por eso una puntuación publicada por un tercero nunca es tu prioridad: esa puntuación describe el defecto en abstracto, con supuestos que casi nunca coinciden con tu despliegue. Tu trabajo es recalcularla con tu exposición, tus datos y tu impacto.
Una consecuencia incómoda de esto es que no existe “seguro”, solo “suficientemente caro de atacar para lo que protege”. Una caja fuerte doméstica y la de un banco son ambas razonables; lo que cambia es el atacante que cada una espera. La pregunta útil nunca es “¿es seguro?”, sino “¿seguro frente a quién, protegiendo qué, y durante cuánto tiempo?”.
4Ejemplo sencillo
# La misma casa, tres fallos distintos CONFIDENCIALIDAD alguien lee tu correspondencia sin abrir la puerta INTEGRIDAD alguien cambia una carta dentro del sobre y la vuelve a cerrar DISPONIBILIDAD alguien echa pegamento en la cerradura y no puedes entrar # Y la cadena causal, en la misma casa amenaza el vecino curioso, con tiempo libre y ganas vulnerabilidad la ventana del baño no cierra bien exploit empujarla hacia arriba con una tarjeta impacto se lleva el portatil con el trabajo de seis meses riesgo probabilidad (alta: vive al lado) x impacto (alto: sin copia)
5Ejemplo técnico
Toma un caso real y frecuente: una API de facturación que devuelve
GET /facturas/{id} sin comprobar que la factura pertenece a quien pregunta.
Aplicado el vocabulario, deja de ser “un bug de permisos” y pasa a ser una frase que se
puede priorizar.
| Término | En este caso |
|---|---|
| Activo | Facturas de todos los clientes: nombre fiscal, dirección, importes. |
| Propiedad | Confidencialidad. La integridad y la disponibilidad no se tocan. |
| Amenaza | Cualquier cliente registrado. No hace falta un atacante sofisticado. |
| Vulnerabilidad | Falta de comprobación de propiedad del objeto (control de acceso roto). |
| Exploit | Cambiar el identificador de la URL e iterar. Sin herramientas. |
| Impacto | Fuga de datos personales y fiscales de toda la cartera. |
| Riesgo | Probabilidad alta (trivial y detectable por accidente) × impacto alto. |
Escrito en una frase: “Cualquier cliente autenticado puede leer las facturas de los demás cambiando el identificador de la URL, lo que expone datos fiscales de toda la cartera.” Esa frase contiene actor, vía, activo y consecuencia. Es lo que hace que un ingeniero sepa qué arreglar y un responsable sepa cuánto vale arreglarlo.
6Diagrama
Ver el mismo diagrama como código Mermaid (editable)
flowchart LR
A[amenaza] --> E[exploit]
V[vulnerabilidad] --> E
E --> AC[activo]
AC --> I[impacto]
I --> R{{riesgo}}
AC --> R
7Código
El vocabulario se vuelve accionable cuando se escribe como datos. Este registro de riesgos mínimo es suficiente para priorizar de verdad, y cabe en un archivo del repositorio:
# riesgos.yml — registro minimo, versionado junto al codigo - id: R-001 frase: > Cualquier cliente autenticado puede leer facturas ajenas cambiando el id de la URL, exponiendo datos fiscales de toda la cartera. propiedad: confidencialidad activo: facturas amenaza: cliente registrado vulnerabilidad: control de acceso roto a nivel de objeto probabilidad: alta # trivial, sin herramientas impacto: alto # datos personales de todos los clientes estado: abierto verificacion: > Test que autentica al usuario A y pide una factura de B; debe dar 404, no 403 (un 403 confirma que el objeto existe).
Lo importante de ese archivo no es el formato. Es el campo verificacion: un
riesgo sin una prueba que falle mientras esté abierto no se puede cerrar, solo se puede
declarar cerrado. Esa diferencia es la que separa la seguridad de la sensación de
seguridad.
8Qué puede salir mal
Confundir la puntuación con la prioridad. Un 9.8 en un servicio inalcanzable importa menos que un 5.3 en el endpoint de login. Copiar la puntuación del boletín es delegar tu criterio a alguien que no conoce tu despliegue.
Proteger una propiedad y declarar el sistema seguro. “Está todo cifrado” es una frase sobre confidencialidad que no dice absolutamente nada sobre integridad ni sobre disponibilidad.
Escribir riesgos sin actor. “Riesgo de fuga de datos” no se puede estimar ni cerrar. Sin saber quién, por dónde y qué se pierde, no es un riesgo: es una preocupación.
Tratar la disponibilidad como problema de otro equipo. Un borrado accidental, una clave perdida y un despliegue mal hecho producen exactamente el mismo resultado que un ataque de denegación de servicio, y son mucho más frecuentes.
9Cómo lo abusaría un atacante
Alguien que ataca no busca “la vulnerabilidad más grave”: busca la cadena más corta hasta algo valioso. Y sabe que las organizaciones priorizan por puntuación, así que el terreno más cómodo son precisamente los defectos aburridos y bien puntuados hacia abajo, que llevan años abiertos porque nunca fueron “críticos”.
La segunda cosa que explota es la confusión de propiedades. Si el equipo cree que el cifrado en reposo es la defensa principal, es probable que la validación de entrada y el control de acceso hayan recibido menos atención, porque “ya está cifrado”. El atacante no rompe el cifrado: entra por la puerta que sí abre, y lee el dato ya descifrado por la propia aplicación.
Y la tercera: la ausencia de registro. Si nadie estimó el riesgo, nadie definió qué habría que ver en los logs cuando ocurriera. Un ataque que nadie está buscando puede durar meses, y ese tiempo es exactamente lo que convierte un acceso pequeño en un incidente grande.
10Cómo defenderlo
- Escribe cada hallazgo como una frase con actor, vía, activo y consecuencia. Si no te sale la frase, aún no entiendes el hallazgo.
- Etiqueta cada riesgo con la propiedad que rompe. Al ver todos juntos vas a descubrir que una de las tres no tiene ningún control.
- Recalcula siempre la probabilidad con tu exposición: ¿es alcanzable desde internet, desde la red interna o solo desde el propio host?
- Exige a cada riesgo una verificación ejecutable. Un test que pasa cuando el problema está arreglado, y solo entonces.
- Guarda el registro en el repositorio, no en una hoja de cálculo. Debe cambiar en el mismo commit que cambia el código.
- Revisa la lista cuando cambie el contexto, no cuando cambie el código: exponer un servicio interno a internet reevalúa todos sus riesgos de golpe.
11Laboratorio
Levanta el laboratorio y comprueba las tres propiedades sobre piezas reales:
cd cursos/seguridad/labs/lab-00-base
docker compose up -d
docker compose ps
Abre http://localhost:8080. Debería responder la aplicación objetivo
a través del proxy. Ahora observa lo que el laboratorio ya te está enseñando:
Clasifica ahora cada servicio: db guarda un activo cuya
confidencialidad importa; proxy es la única frontera hacia el host;
monitor existe para poder demostrar qué pasó. Cuando termines:
docker compose down -v
12Ejercicios
Clasifica cinco incidentes
Para cada uno, di qué propiedad de la tríada se rompió principalmente: (1) un empleado descarga la lista de clientes antes de irse; (2) un despliegue borra la tabla de pedidos; (3) un proveedor cambia el IBAN en una factura por correo; (4) el certificado TLS caduca y la web deja de cargar; (5) un log de depuración imprime tokens de sesión.
(1) confidencialidad; (2) disponibilidad, y también integridad si había datos derivados; (3) integridad, con impacto económico directo; (4) disponibilidad; (5) confidencialidad en potencia.
Clave: C · D · I · D · C. El caso (2) admite dos respuestas y por eso es el interesante: el dato no está y además lo que se calculó a partir de él ya no cuadra.
Escribe tres riesgos del laboratorio
Con el lab-00-base levantado, escribe tres riesgos en el formato de
riesgos.yml. Uno por cada propiedad de la tríada. Cada frase debe
contener actor, vía, activo y consecuencia, y cada riesgo debe llevar su campo
verificacion.
docker-compose.yml: la contraseña de postgres está en claro y el proxy es la única pieza que cruza redes.db_datos cuando alguien ejecuta down -v por costumbre.curl desde el host a un puerto que no debería existir; debe fallar.
Confidencialidad: “Cualquiera con acceso al repositorio obtiene la contraseña de
postgres, que está en claro en el compose, y puede leer la base si además llega a
la red interna.” Verificación: grep -r POSTGRES_PASSWORD no debe
encontrar un valor literal.
Integridad: “Cualquier proceso del host que alcance el proxy puede escribir en la app sin autenticación, alterando lo que sirve.” Verificación: una petición de escritura sin credenciales debe responder 401.
Disponibilidad: “Un docker compose down -v ejecutado por costumbre
destruye el volumen db_datos y con él todo el estado del ejercicio.”
Verificación: existe un procedimiento de export antes de destruir.
Clave: lo que se corrige aquí no es el laboratorio (es deliberadamente imperfecto) sino la frase. Si a alguna le falta el actor, reescríbela.
Defiende una despriorización
Tienes dos hallazgos. A: ejecución remota de código, puntuación 9.8, en una
herramienta interna que solo escucha en 127.0.0.1 de una máquina de
compilación sin usuarios interactivos. B: enumeración de usuarios en el endpoint de
recuperación de contraseña, puntuación 5.3, en producción. Escribe el argumento
técnico por el que B va primero, y también las tres condiciones que, si cambian,
invierten esa decisión.
B primero: es alcanzable por cualquiera desde internet, sin autenticación y sin herramientas, y alimenta directamente el ataque siguiente (relleno de credenciales contra una lista de usuarios ya confirmada). A tiene impacto mayor pero probabilidad de alcance muy baja: requiere estar ya dentro de esa máquina, momento en el que el atacante ya tiene lo que la ejecución remota le daría.
Se invierte si: (1) la herramienta de A deja de escuchar solo en localhost o se publica tras un proxy; (2) aparece otro fallo que dé ejecución en esa máquina, con lo que A pasa a ser el eslabón de escalada; (3) la máquina de compilación empieza a custodiar credenciales de despliegue, subiendo el impacto de A por encima de cualquier consideración de probabilidad.
Clave: la respuesta correcta no es “B” sino el razonamiento. Un argumento que no nombre las condiciones de reversión está incompleto.
Red Team · encuentra la pieza que rompe el aislamiento
Dentro del lab-00-base, y solo dentro de él: demuestra con comandos que
el contenedor atacante no tiene salida a internet, y luego encuentra la
única modificación de una línea en el docker-compose.yml que se la daría.
Aplícala, demuéstralo, y revierte.
curl con -m 5 hacia fuera, y la resolución DNS por separado.proxy y que los demás no tienen.
Añadir lab_borde a la lista de redes del servicio atacante
le da salida, porque esa red no tiene internal: true. Basta una línea.
Es exactamente el error que convierte un laboratorio en un problema para terceros,
y por eso conviene haberlo visto de forma controlada una vez.
# antes atacante: networks: - lab_interna # despues (NO dejarlo asi) atacante: networks: - lab_interna - lab_borde
Clave: revierte el cambio y vuelve a comprobarlo. Un laboratorio ofensivo con salida a internet deja de ser un laboratorio.
Blue Team · qué habría que ver en los logs
Genera tráfico contra el proxy, incluido al menos un 404, y localiza esas peticiones
en docker compose logs proxy. Después responde: con el formato de log
actual, ¿podrías reconstruir quién hizo qué? Enumera los campos que faltan para
investigar el riesgo de confidencialidad que escribiste en el ejercicio medio.
combined de nginx trae IP, hora, petición, código y agente. Compáralo con lo que necesitarías.
Con combined puedes reconstruir qué se pidió y cuándo,
pero no quién. Para investigar una fuga faltan al menos: identificador de
usuario o de sesión, un identificador de correlación propagado entre servicios,
el resultado de la autorización (no solo el código HTTP) y el identificador del
objeto accedido.
Clave: el entregable es la lista de campos que faltan. Guárdala: en la fase 14 vas a implementarla y a escribir la regla de detección que la usa.
Architecture Challenge · el registro de riesgos como parte del repositorio
Diseña cómo integrarías riesgos.yml en un repositorio real de forma que
no se pudra. Define: dónde vive, quién lo modifica, qué comprobación automática lo
valida, qué pasa en el pipeline cuando un riesgo abierto no tiene verificación, y
cómo evitas que se convierta en burocracia que todo el mundo saltea.
Una propuesta razonable: riesgos.yml en la raíz, propiedad del equipo
que mantiene el servicio y revisable por cualquiera. Un validador de esquema en el
pipeline que bloquea si un riesgo tiene formato inválido o le falta actor,
porque eso es barato de arreglar, y que solo avisa si un riesgo abierto no
tiene todavía verificación, con un plazo. La revisión se dispara por cambio de
contexto —una nueva exposición pública, un dato nuevo, una integración— y no por
calendario, para que no degenere en un ritual trimestral que nadie lee.
Clave: no hay una única respuesta. Se evalúa que distingas qué bloquea y qué avisa, y que el disparador de revisión sea un cambio real y no una fecha.
13Preguntas de comprensión
Un atacante borra los logs después de entrar. ¿Qué propiedad rompe ese borrado en concreto?
Los logs son un activo, y de los caros: sin ellos no puedes reconstruir el incidente ni demostrar su alcance.
¿Cuál de estas frases es un riesgo bien escrito?
Solo la tercera tiene actor, vía, activo y consecuencia. Las otras dos no se pueden estimar ni cerrar.
Cifras la base de datos en reposo. ¿Contra qué escenario NO te protege?
La aplicación tiene la clave: el cifrado se aplica y se retira de forma transparente, así que la inyección lee el dato en claro.
¿Por qué la puntuación publicada de una vulnerabilidad no es tu prioridad?
Esa puntuación es una propiedad del defecto. El riesgo es una propiedad de tu sistema, y solo tú puedes calcularlo.
14Reto adicional
Modela el riesgo de este propio sitio
Este curso guarda tu progreso y tus notas en localStorage, sin servidor y
sin cuenta. Escribe los riesgos de esa decisión con el vocabulario de la lección:
¿qué propiedad protege bien?, ¿cuál protege mal?, ¿quién es la amenaza realista?, y
¿qué mitigación propondrías que no implique añadir un backend?
Confidencialidad: excelente por construcción, porque no hay datos en ningún servidor que alguien pueda filtrar; el coste es que otro usuario del mismo navegador sí puede leer tus notas. Disponibilidad: mala, y esa es la amenaza realista: limpiar datos del sitio, cambiar de navegador o de máquina destruye el progreso sin que intervenga nadie. Integridad: nadie más la ataca, pero tampoco hay forma de detectar una modificación.
La mitigación implementada es la exportación a JSON del panel de progreso. Es correcta pero insuficiente por sí sola: depende de que te acuerdes. Un aviso automático cada N lecciones completadas la vuelve efectiva sin añadir servidor.
Clave: lo interesante del ejercicio es que la decisión de arquitectura resuelve una propiedad y empeora otra. Casi todas lo hacen; el trabajo es elegir cuál te conviene empeorar.
15Resumen
- Un sistema falla de tres formas: alguien ve lo que no debe, alguien cambia lo que no debe, o nadie puede usarlo.
- Las defensas de una propiedad no cubren las otras. “Está cifrado” no dice nada sobre integridad ni disponibilidad.
- Amenaza y vulnerabilidad solo producen daño juntas; el exploit es lo que las une.
- El riesgo no es una propiedad del sistema: es una estimación tuya, y por eso la puntuación de un tercero nunca es tu prioridad.
- Un riesgo bien escrito tiene actor, vía, activo y consecuencia, y una verificación ejecutable.
- Sin verificación, un riesgo no se cierra: solo se declara cerrado.
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.