Secure SDLC: integrar la seguridad en el ciclo de vida del software

Secure SDLC: integrar la seguridad en el ciclo de vida del software

Durante años se aprendía a escribir software y, al final del proceso, se añadía un paso de revisión de seguridad. Ese modelo, conocido informalmente como security at the end, tiene un problema que no se arregla con más horas de revisión: cuando la seguridad entra en la última fase, ya no se puede cambiar la arquitectura, ni el modelo de datos, ni las dependencias, ni los contratos de API. Lo que queda es cubrir con parches un diseño que nunca se diseñó pensando en la seguridad.

El Secure SDLC propone lo contrario: cada fase del ciclo de vida tiene actividades de seguridad, artefactos y responsables. La diferencia no está en las herramientas, sino en el momento en el que se toman las decisiones.

Las fases y su responsable de seguridad

FaseActividad de seguridadEvidencia que se produce
RequisitosQué se protege, quién es el atacante, qué datos son sensiblesLista de amenazas y criterios de aceptación
DiseñoModelado de amenazas y revisión de arquitecturaDiagrama de flujos de datos y mitigaciones
ImplementaciónEstándares de código y análisis estáticoInforme de SAST sin hallazgos críticos
PruebasPruebas de seguridad funcionales y automáticasEvidencia de DAST y de cobertura de casos
DespliegueVerificación de artefactos y configuración seguraSBOM, escaneo de imagen y configuración
OperaciónMonitorización, respuesta a incidentes y parcheoRegistros, alertas y tiempos de corrección

La clave es que la evidencia existe en cada fase. Una organización que solo puede mostrar el informe de la última fase no tiene un Secure SDLC, tiene un control de calidad tardío.

Fase 1: requisitos

El error más común de esta fase es escribir requisitos de seguridad como frases genéricas: el sistema debe ser seguro. Eso no se puede verificar ni implementar.

Los requisitos de seguridad deben ser específicos y comprobables, idealmente escritos junto a los criterios de aceptación de la historia de usuario.

Requisito mal escritoRequisito verificable
Las contraseñas deben ser segurasLas contraseñas se almacenan con Argon2id y nunca en texto plano ni con MD5
Los datos personales están protegidosLos campos marcados como personales se cifran en reposo y no aparecen en logs
Solo administradores ven los reportesCada consulta de reportes comprueba el rol en el servidor, no en el cliente

En esta fase conviene también aclarar los límites de confianza: qué datos entran desde el exterior, qué se considera interno y qué componentes de terceros se integran. Esa información es la base del modelado de amenazas de la fase siguiente.

Fase 2: diseño y modelado de amenazas

El modelado de amenazas responde a cuatro preguntas que se pueden aplicar en una reunión de media hora:

  1. ¿Sobre qué estamos trabajando?
  2. ¿Qué puede salir mal?
  3. ¿Qué se va a hacer para evitarlo?
  4. ¿Suficiente?

Para la segunda pregunta, la metodología STRIDE ofrece un catálogo de amenazas por categoría que evita empezar de cero:

CategoríaPregunta que ayuda a responderEjemplo en una tienda en línea
Spoofing (suplantación)¿Puede alguien hacerse pasar por otro?Login sin verificación de identidad
Tampering (manipulación)¿Se pueden alterar los datos en tránsito o reposo?Modificar el precio de un carrito
Repudiation (repudio)¿Podemos demostrar quién hizo cada acción?Un cambio de precio sin registro de autor
Information disclosure¿Se puede ver lo que no le corresponde?Endpoint que devuelve datos de otro cliente
Denial of service¿Se puede agotar el servicio?Búsqueda sin paginación ni límites
Elevation of privilege¿Puede alguien obtener más permisos de los fijados?Un endpoint sin control de rol

El resultado debe quedar documentado junto al diseño, y cada amenaza con mitigación conocida se traduce en un requisito o en una tarea de implementación. Un modelo de amenazas que no genera tareas se convierte en un documento decorativo que nadie vuelve a leer.

En esta fase también se decide la superficie de dependencias: qué librerías entran, de dónde salen, qué permisos necesitan y cómo se van a actualizar. Generar el inventario de software (SBOM) desde el principio es mucho más barato que reconstruirlo en una auditoría.

Fase 3: implementación

En la fase de codificación la seguridad se apoya en dos columnas: evitar el origen del problema y detectar el error lo antes posible.

No construir el fallo

Los dos errores que más se repiten en código propio son la concatenación de entradas en consultas SQL y el acceso a archivos sin validar la ruta.

// Vulnerable: la entrada del usuario se concatena en la consulta
String sql = "SELECT * FROM usuarios WHERE email = '" + email + "'";

try (Statement statement = connection.createStatement();
     ResultSet rs = statement.executeQuery(sql)) {
    // ...
}

Un correo con un apóstrofe ya rompe la consulta, y con una unión se puede leer información de otras tablas. La corrección no es filtrar caracteres, es usar consultas parametrizadas:

// Seguro: los valores viajan como parametros, nunca como codigo SQL
String sql = "SELECT id, nombre, email FROM usuarios WHERE email = ?";

try (PreparedStatement statement = connection.prepareStatement(sql)) {
    statement.setString(1, email);

    try (ResultSet rs = statement.executeQuery()) {
        // ...
    }
}

El segundo patrón, la traversal de rutas, aparece en cuanto la aplicación acepta un nombre de archivo:

// Vulnerable: ../../etc/passwd escaparia del directorio permitido
String ruta = Paths.get("/var/www/uploads", nombreArchivo).toString();
return Files.readAllBytes(Path.of(ruta));

La versión segura normaliza ambas rutas y comprueba que el resultado sigue dentro del directorio permitido:

Path base = Path.of("/var/www/uploads").toAbsolutePath().normalize();
Path destino = base.resolve(nombreArchivo).normalize();

if (!destino.startsWith(base)) {
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Ruta no permitida");
}

return Files.readAllBytes(destino);

normalize() resuelve los .. y startsWith verifica el resultado. Es una comprobación de cuatro líneas que evita una lectura arbitraria de archivos.

Detectar el error temprano

El análisis estático de aplicaciones (SAST) examina el código sin ejecutarlo. Una regla que busca credenciales en el código puede verse así:

# .semgrep/secrets.yml
rules:
  - id: hardcoded-credential
    languages: [java, javascript, typescript, python, php]
    severity: ERROR
    message: Posible credencial escrita directamente en el codigo
    patterns:
      - pattern-either:
          - pattern: $SECRET = "..."
          - pattern: password = "..."
          - pattern: $APIKEY = "..."
    metadata:
      cwe: "CWE-798: Use of Hard-coded Credentials"

Lo interesante de esta clase de herramientas es que el conjunto de reglas es texto plano, versionable y revisable en una revisión de código. Un SAST no adivina, aplica reglas, y eso se puede discutir con el equipo.

Fase 4: pruebas de seguridad

Las pruebas funcionales verifican que el sistema haga lo que debe. Las pruebas de seguridad verifican que no haga lo que no debe, y hay que escribirlas explícitamente: no aparecen por accidente.

El caso más rentable es la matriz de autorización: por cada endpoint, comprobar quién puede invocarlo.

EndpointAnónimoClienteAdministrador
GET /api/perfilNoSí, solo el suyoSí, todos
GET /api/facturasNoSí, solo las suyasSí, todas
DELETE /api/facturas/:idNoPropietarioSí
GET /api/admin/usuariosNoNoSí

Cada celda es un caso de prueba. Esta tabla detecta la vulnerabilidad más común en APIs modernas, la referencia a objetos directos (IDOR o BOLA), que existe cuando el servidor filtra por propietario y olvida comprobar que el recurso pertenece a quien pregunta.

@Test
void unClienteNoPuedeLeerLaFacturaDeOtro() {
    // Sesion del cliente 1
    var sesion = clienteAutenticadoComo("cliente-1");

    // La factura 900 pertenece al cliente 2
    assertThrows(ResponseStatusException.class, () -> sesion.get("/api/facturas/900"));
}

Además de las pruebas escritas a mano, el análisis dinámico (DAST) examina la aplicación en ejecución. ZAP puede ejecutarse de forma continua en integración continua:

name: Security

on:
  push:
    branches: [main]

jobs:
  dast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Arrancar la aplicacion
        run: docker compose up -d --wait

      - name: Escaneo dinamico con OWASP ZAP
        uses: ghcr.io/zaproxy/zaproxy-action@0
        with:
          configFile: zaf.yaml

La diferencia con un escaneo ejecutado a mano es que el resultado acaba en el pipeline, junto al resto de evidencias de la entrega. Si el escaneo falla, la entrega se detiene.

Fase 5: despliegue

Antes de publicar hay que verificar lo que se va a publicar, no solo el repositorio. Un artefacto puede contener archivos que nunca se versionaron, pero que sí acaban dentro de la imagen o del paquete.

# Inventario de dependencias en formato CycloneDX
syft dir:. -o cyclonedx-json > sbom.json

# Secretos en todo el historial del repositorio
gitleaks git --redact --no-banner

# Vulnerabilidades conocidas en la imagen ya construida
trivy image --severity HIGH,CRITICAL mi-imagen:1.4.0

La gestión de dependencias también se configura como código. En lugar de esperar a que alguien actualice, el sistema propone las actualizaciones de forma continua:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    open-pull-requests-limit: 5
  - package-ecosystem: pip
    directory: /
    schedule:
      interval: weekly

Sobre la imagen y el entorno, la revisión se concentra en cuatro puntos: usuario de ejecución sin privilegios, sistema de archivos de solo lectura cuando es posible, ausencia de secretos en variables de imagen y configuración de TLS y cabeceras en el servidor web.

Fase 6: operación y mantenimiento

La seguridad de un sistema que ya está en producción tiene un componente operativo que ninguna fase anterior cubre: la capacidad de detectar y corregir. La clave de rotación, la reversión de un despliegue y el análisis forense de un incidente tienen que estar escritos antes de que ocurran.

Las señales mínimas son un registro de accesos fiable, alertas sobre cambios de configuración y una ruta de escalado conocida. La rotación periódica de credenciales y la actualización de dependencias son tareas de mantenimiento, no accidentales: si no están en el calendario, no se hacen.

Frameworks de referencia

Los pasos anteriores no son una invención. Existen marcos públicos que describen conjuntos similares de prácticas:

MarcoEnfoque
NIST SSDF (SP 800-218)Prácticas de seguridad en cada fase del ciclo de vida
OWASP SAMMModelo de madurez para medir el nivel de la organización
OWASP ASVSRequisitos de verificación concretos y comprobables
ISO 27001Control de la seguridad de la información en la organización

Marcos como NIST SSDF y OWASP ASVS son especialmente útiles porque traducen la seguridad a listas comprobables, que es justo lo que falta cuando el proceso se apoya en la buena voluntad del equipo.

Métricas

Medir sin cuidado genera theatre. Las tres señales que mejor funcionan:

  • Porcentaje de las entregas que pasan por análisis estático, dinámico y de dependencias en el pipeline.
  • Tiempo medio entre la publicación de una vulnerabilidad y su corrección.
  • Porcentaje de amenazas del modelo que tienen mitigación implementada y verificada.

Las tres miden el proceso, no la intención. Un equipo con un porcentaje alto de amenazas modeladas y sin mitigaciones implementadas tiene un documento, no un Secure SDLC.

Errores frecuentes

  • La seguridad al final del proyecto. Cuando la revisión llega tarde, ya no se puede cambiar el diseño y solo quedan parches.
  • Checklists que nadie cumple. Una lista de treinta puntos aplicada al final de cada entrega se cumple en cinco minutos. Las listas de verificación solo funcionan si se reducen a lo imprescindible y se validan de forma automática.
  • Modelar amenazas sin convertir nada en trabajo. Cada amenaza necesita una mitigación, un responsable y una fecha.
  • Escáneres sin contexto. Una herramienta que marca veinte hallazgos irrelevantes y nadie revisa, o un hallazgo crítico entre ellos, porque el resultado siempre es el mismo.
  • Ignorar los artefactos. La imagen, el paquete y los archivos de configuración son parte del producto y también se revisan.
  • Medir el estado en lugar del proceso. El número de vulnerabilidades pendientes depende del inventario, no solo del esfuerzo.

Conclusión

El Secure SDLC no es un conjunto de herramientas, es una forma de tomar decisiones. Su valor está en que cada decisión de seguridad se toma en la fase en la todavía puede cambiar el resultado:

MomentoDecisión que se toma y que después ya no se puede corregir
RequisitosQué datos son sensibles y qué se protege
DiseñoCómo se aíslan los componentes y dónde están los límites
ImplementaciónSi la entrada se trata como dato o como código
PruebasSi la autorización se comprueba de forma automatizada
DespliegueSi el artefacto que se publica es el que se revisó

La transición de un modelo tardío a uno integrado suele empezar con un cambio pequeño: mover el análisis de dependencias y el escaneo de secretos al pipeline, y escribir las primeras pruebas de autorización. Ninguna de las dos tareas necesita un proceso nuevo, y a partir de ahí el resto tiene dónde apoyarse.

© Arturo Juárez Monroy