Fundamentos · Lección 01

CIA, riesgo, amenaza, vulnerabilidad y exploit

Casi todas las discusiones de seguridad que acaban mal empiezan igual: dos personas usando la palabra riesgo para decir cosas distintas. Antes de romper nada hace falta un vocabulario que no se doble. Esta lección fija seis términos con precisión suficiente para discutir con un auditor, priorizar trabajo real y, sobre todo, escribir tickets que alguien pueda cerrar. Al final vas a levantar el laboratorio base y a clasificar sus piezas con ese vocabulario.

Al terminar sabrás

  • Separar confidencialidad, integridad y disponibilidad, y decir cuál se rompió en un incidente concreto.
  • Distinguir amenaza, vulnerabilidad, exploit, impacto y riesgo sin usarlos como sinónimos.
  • Escribir un riesgo en una frase que contenga actor, vía, activo y consecuencia.
  • Priorizar dos hallazgos usando probabilidad e impacto en vez de intuición.
  • Explicar por qué “esta vulnerabilidad es crítica” no significa nada sin contexto.
  • Levantar el laboratorio base y clasificar cada servicio por lo que protege.
Antes de empezar · qué sabes ya

Tres preguntas rápidas. No puntúan para completar la lección: sirven para que la explicación se ajuste a lo que ya traes.

Un atacante cambia el precio de un producto en tu base de datos. ¿Qué propiedad se rompió principalmente?

Integridad: el dato sigue estando y sigue siendo legible, pero ya no es el que debía ser.

¿“Vulnerabilidad” y “riesgo” son lo mismo?

La vulnerabilidad es una propiedad del sistema. El riesgo es una estimación que además necesita un actor y un impacto.

Tienes una vulnerabilidad con puntuación 9.8 en un servicio que solo escucha en 127.0.0.1 de una máquina sin usuarios. ¿Qué haces?

La puntuación es del defecto, no de tu sistema. Sin contexto de exposición y de impacto, no es una prioridad.

0 / 3

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.

El objetivo real

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

Los seis términos
  • 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)
El vocabulario completo cabe en una casa. La dificultad no está en entenderlo, sino en no mezclarlo cuando la conversación se acelera.

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érminoEn este caso
ActivoFacturas de todos los clientes: nombre fiscal, dirección, importes.
PropiedadConfidencialidad. La integridad y la disponibilidad no se tocan.
AmenazaCualquier cliente registrado. No hace falta un atacante sofisticado.
VulnerabilidadFalta de comprobación de propiedad del objeto (control de acceso roto).
ExploitCambiar el identificador de la URL e iterar. Sin herramientas.
ImpactoFuga de datos personales y fiscales de toda la cartera.
RiesgoProbabilidad 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

Cadena de riesgo: amenaza más vulnerabilidad, vía exploit, produce impacto sobre un activo; probabilidad por impacto da el riesgo amenaza vulnerabilidad exploit activo impacto riesgo solo juntas probabilidad × impacto
Sin los dos eslabones de la izquierda no hay cadena. El riesgo no es un nodo del sistema: es lo que tú calculas al final.
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

Los cuatro errores clásicos

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

Vista desde el otro lado

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

Qué hacer con esto mañana
  • 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

Estado: laboratorio local lab-00-base
Targethttp://localhost:8080
Autorizaciónsistema local creado por ti para entrenamiento
Todo lo que se practica en este curso se practica aquí dentro. Ninguna técnica de este material debe apuntarse a un sistema que no sea tuyo o para el que no tengas autorización escrita.

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:

comprobar el aislamiento
docker compose exec atacante sh -c "curl -m 5 -sS https://example.com" curl: (6) Could not resolve host: example.com # correcto: lab_interna tiene internal: true, no hay salida docker compose exec proxy sh -c "wget -qO- http://app/ | head -2" <!DOCTYPE html> <html lang="es"> # el proxy si llega: es la unica pieza con un pie en cada red

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.

Pregúntate qué le pasó al dato: ¿alguien lo vio, alguien lo cambió, o nadie pudo usarlo?
Un borrado accidental rompe disponibilidad aunque no haya atacante. El daño no necesita intención.
El caso (5) no es un incidente todavía: es una vulnerabilidad que expone confidencialidad si alguien lee el log.
Solució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.

Mira el docker-compose.yml: la contraseña de postgres está en claro y el proxy es la única pieza que cruza redes.
Para disponibilidad, piensa en qué pasa con db_datos cuando alguien ejecuta down -v por costumbre.
Una verificación válida para el riesgo de exposición es un curl desde el host a un puerto que no debería existir; debe fallar.
Solución

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.

Separa los dos factores: la puntuación habla del defecto, tú tienes que hablar de probabilidad de alcance.
Pregunta quién puede llegar a cada uno hoy, y con cuánto esfuerzo.
Las condiciones que invierten la decisión suelen ser de contexto: exposición, encadenamiento con otro fallo, y valor de lo que hay detrás.
Solució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.

Empieza midiendo: curl con -m 5 hacia fuera, y la resolución DNS por separado.
El aislamiento no lo da el contenedor, lo da la red a la que está conectado.
Mira qué red tiene el proxy y que los demás no tienen.
Solución

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.

El formato combined de nginx trae IP, hora, petición, código y agente. Compáralo con lo que necesitarías.
Piensa en correlación: si una petición pasa por proxy y app, ¿cómo sabes que son la misma?
Falta identidad. Una IP no es un usuario, sobre todo detrás de un proxy.
Solución

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.

Todo documento de seguridad que vive fuera del repositorio se desincroniza en un trimestre.
Si la comprobación bloquea siempre, la gente la desactiva. Piensa en qué debe avisar y qué debe bloquear.
Un riesgo con verificación es un test. Un riesgo sin verificación es una nota. Trátalos distinto en el pipeline.
Solución

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

Comprobació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.

0 / 4

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?

Sin servidor no hay base de datos que filtrar. Eso resuelve una propiedad de golpe.
La amenaza realista no es un atacante: es tú mismo borrando los datos del navegador.
Mira el panel de progreso. Ya existe una mitigación implementada; nómbrala y di si es suficiente.
Solución

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

Puntos clave
  • 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.
¿Qué propiedad rompe alguien que modifica un dato sin ser detectado?
Integridad. El dato sigue disponible y sigue siendo confidencial, pero ya no es el que debía ser.
¿Qué le falta a la frase “riesgo alto de fuga de datos”?
Actor, vía, activo y consecuencia. Sin ellos no se puede estimar la probabilidad ni definir una verificación.
¿Por qué una vulnerabilidad con puntuación 9.8 puede no ser prioritaria?
Porque la puntuación mide el defecto en abstracto. Si en tu despliegue no es alcanzable, la probabilidad es baja y el riesgo también.
¿Qué convierte un riesgo en algo que se puede cerrar?
Una verificación ejecutable: una prueba que falla mientras el problema existe y pasa cuando está resuelto.

16Mis notas

Cierre de la lección

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.