Ciberseguridad · Capítulo 17
Seguridad en el Desarrollo de Software: DevSecOps y Código Seguro
Detectar un bug de seguridad en diseño cuesta $80. El mismo bug en producción cuesta $7,600. La seguridad debe construirse en el software desde el primer día, no añadirse al final.
El Paradigma Shift-Left
Tradicional mente, la seguridad se añadía al final del ciclo de desarrollo: el equipo de seguridad auditaba el software justo antes de su lanzamiento, encontraba decenas de vulnerabilidades, y el equipo de desarrollo tenía que re-trabajar código ya finalizado bajo presión de tiempo. El resultado era predecible: muchas vulnerabilidades quedaban sin corregir, se lanzaban parches de emergencia semanas después, y los costos se disparaban.
El paradigma Shift-Left mueve las actividades de seguridad hacia las etapas más tempranas del proceso de desarrollo. El principio es simple: es mucho más barato, rápido y fácil corregir un problema cuando aún estás diseñando o codificando que después de desplegar en producción.
El costo de corregir vulnerabilidades según la fase: Según el IBM Systems Sciences Institute, corregir un defecto en la fase de diseño cuesta $80. El mismo defecto en implementación: $240. En pruebas: $960. En producción: $7,600. Una brecha de seguridad real en producción: millones de dólares en multas, remediación y daño reputacional.
El Ciclo de Vida Seguro de Desarrollo de Software (Secure SDLC)
Fase 1: Requisitos
Los requisitos de seguridad deben definirse junto con los requisitos funcionales, no después. Ejemplos de requisitos de seguridad:
- "El sistema debe requerir MFA para todos los usuarios con acceso a datos de pago"
- "Todos los datos de clientes deben cifrarse en reposo con AES-256"
- "El sistema no debe almacenar contraseñas en texto plano ni como MD5/SHA-1"
- "Las sesiones deben expirar automáticamente después de 30 minutos de inactividad"
- "Todos los intentos de login fallidos deben registrarse con IP, timestamp y usuario"
Fase 2: Diseño — Modelado de Amenazas
El modelado de amenazas es el proceso sistemático de identificar qué puede salir mal en un sistema antes de construirlo. El framework más usado es STRIDE, desarrollado por Microsoft:
| Letra | Amenaza | Ejemplo | Control |
| S | Spoofing (Suplantación) | Atacante se hace pasar por usuario legítimo | Autenticación fuerte, MFA |
| T | Tampering (Manipulación) | Modificar datos en tránsito o en base de datos | Integridad: HTTPS, hashes, firmas digitales |
| R | Repudiation (Repudio) | Usuario niega haber realizado una acción | Logs firmados digitalmente, auditoría |
| I | Information Disclosure (Divulgación) | Acceso no autorizado a datos confidenciales | Cifrado, control de acceso, minimización de datos |
| D | Denial of Service (DoS) | Hacer el sistema inaccesible para usuarios legítimos | Rate limiting, redundancia, auto-scaling |
| E | Elevation of Privilege (Escalada) | Usuario sin privilegios gana acceso de administrador | Principio de mínimo privilegio, separación de roles |
Diagramas de Flujo de Datos para Modelado de Amenazas
El modelado de amenazas comienza dibujando un DFD (Data Flow Diagram) del sistema. Para un sistema de login:
Ejemplo de DFD para un sistema de login:
- Entidades externas: Usuario (desde internet)
- Procesos: Servidor web (valida input), Servidor de autenticación (verifica credenciales)
- Almacenes de datos: Base de datos de usuarios (credenciales hasheadas)
- Flujos de datos: usuario→web (credenciales), web→auth (solicitud), auth→DB (consulta)
- Límites de confianza: Internet / DMZ / Red interna — el tráfico que cruza estos límites es el más riesgoso
Para cada flujo que cruza un límite de confianza, aplicas STRIDE y te preguntas: ¿cómo podría ser suplantado? ¿manipulado? ¿denegado?
Fase 3: Implementación — Prácticas de Código Seguro
1. Validación de Input
La regla de oro: nunca confíes en el input del usuario. Valida siempre en el servidor, independientemente de lo que hagas en el cliente:
- Valida tipo (¿es un número entero?), longitud (¿menos de 255 caracteres?), formato (¿es un email válido?), rango (¿está la edad entre 0 y 150?)
- Usa listas blancas (permite solo lo que sabes que es válido) en lugar de listas negras (bloquea lo que sabes que es malo — siempre incompletas)
- La validación del lado cliente (JavaScript) es solo para UX — cualquier atacante puede bypasearla con Burp Suite en segundos
2. Codificación de Output (Output Encoding)
Para prevenir XSS (Cross-Site Scripting), el output debe codificarse según el contexto donde se inserta:
Código vulnerable (XSS):
<p>Hola, <?php echo $_GET['nombre']; ?></p>
Si el atacante pone <script>document.cookie</script> como nombre, el script se ejecuta en el navegador de la víctima.
Código seguro:
<p>Hola, <?php echo htmlspecialchars($_GET['nombre'], ENT_QUOTES, 'UTF-8'); ?></p>
htmlspecialchars convierte: < → <, > → >, " → " — el script se muestra como texto, no se ejecuta.
3. Consultas Parametrizadas (Prepared Statements)
Código vulnerable (SQL Injection):
$query = "SELECT * FROM users WHERE email='" . $email . "' AND password='" . $password . "'";
Código seguro (consulta parametrizada en PHP/PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND password_hash = ?");
$stmt->execute([$email, hash_password($password)]);
Con prepared statements, el valor del parámetro nunca se interpreta como SQL — es siempre tratado como dato, nunca como comando.
4. Manejo Seguro de Errores
- Al usuario: muestra siempre mensajes genéricos ("Ha ocurrido un error. Por favor intenta de nuevo.")
- En logs internos: registra el error completo con stack trace, contexto y timestamp
- Nunca mostrar al usuario: nombres de tablas, rutas del sistema, versiones de frameworks, stack traces
- En producción, desactivar todos los modos de debug de frameworks (Django DEBUG=False, Laravel APP_DEBUG=false)
5. Gestión de Secretos
Regla absoluta: NUNCA hardcodear credenciales en el código fuente. Cada semana, miles de desarrolladores accidentalmente suben API keys, contraseñas de bases de datos y tokens de acceso a repositorios GitHub públicos. Bots automatizados escanean GitHub constantemente y explotan estas credenciales en minutos. AWS desactiva automáticamente las access keys que detecta en código público, pero no todos los proveedores lo hacen.
Las alternativas correctas para gestionar secretos:
- Variables de entorno: el secreto vive en el entorno de ejecución, no en el código. En el código:
os.environ.get('DATABASE_PASSWORD')
- .env files con .gitignore: archivo local con variables de entorno, nunca versionado en git. Añadir
.env al .gitignore desde el primer commit.
- HashiCorp Vault: gestión centralizada de secretos con rotación automática, auditoría y acceso dinámico — las aplicaciones reciben credenciales temporales generadas al momento, que expiran automáticamente
- git-secrets hook: hook pre-commit que escanea el código antes de cada commit buscando patrones de API keys, contraseñas y tokens
- AWS Secrets Manager / Azure Key Vault / GCP Secret Manager: servicios gestionados de cada cloud
Fase 4: Pruebas de Seguridad
SAST — Static Application Security Testing
Analiza el código fuente sin ejecutarlo, buscando patrones de vulnerabilidades conocidas:
- SonarQube: open source, integración con IDEs (IntelliJ, VS Code) y CI/CD (GitHub Actions, Jenkins). Detecta más de 600 patrones de vulnerabilidades en Java, Python, JavaScript, C#, PHP y más. Tiene un dashboard de deuda técnica de seguridad.
- Semgrep: muy rápido, reglas altamente personalizables en YAML. Permite escribir reglas específicas para tu codebase (ej.: "detecta cualquier lugar donde usemos MD5 para contraseñas").
- Checkmarx: solución empresarial, más completa y con menos falsos positivos, pero costosa.
DAST — Dynamic Application Security Testing
Prueba la aplicación en ejecución enviando solicitudes maliciosas y observando las respuestas:
- OWASP ZAP en CI/CD: puede ejecutarse automáticamente contra un entorno de staging en cada despliegue, generando un reporte de vulnerabilidades
- Burp Suite Enterprise: versión de servidor de Burp Suite, permite programar escaneos automáticos
SCA — Software Composition Analysis
- Snyk: verifica automáticamente las dependencias (npm, pip, Maven, Go modules) contra la base de datos de CVEs. Integrado como GitHub Action, puede bloquear PRs que introduzcan dependencias con vulnerabilidades conocidas. También ofrece fix automático sugiriendo la versión parcheada.
- OWASP Dependency-Check: alternativa gratuita y open source
Fase 5: Despliegue Seguro
- Infrastructure as Code (IaC) escaneado con Checkov antes de aplicar — detecta buckets S3 públicos, security groups abiertos, cifrado desactivado en archivos Terraform o CloudFormation
- Imágenes Docker escaneadas con Trivy antes de subir al registry
- Security gates en el pipeline: el pipeline CI/CD falla automáticamente si se detectan vulnerabilidades críticas (CVSS ≥ 9.0) — el despliegue no puede continuar hasta resolverlas
Lista de Verificación para Code Review de Seguridad
Cada revisión de código (pull request) debería incluir estos 10 puntos de seguridad:
- ¿Todo el input del usuario es validado en el servidor (tipo, longitud, formato, rango)?
- ¿Todas las consultas a bases de datos usan prepared statements o ORMs?
- ¿El output se codifica apropiadamente según el contexto (HTML, JSON, JavaScript)?
- ¿Las contraseñas se hashean con bcrypt, Argon2 o scrypt (nunca MD5/SHA1)?
- ¿Hay algún secreto (API key, contraseña, token) hardcodeado en el código?
- ¿Los mensajes de error exponen información sensible del sistema?
- ¿Se verifican los permisos de autorización en cada endpoint que accede a datos sensibles?
- ¿Se registran los eventos de seguridad relevantes (login, cambio de contraseña, acceso a datos sensibles)?
- ¿Hay alguna dependencia nueva que no ha sido escaneada en busca de CVEs?
- ¿El código nuevo introduce alguna superficie de ataque que no existía antes?
El Programa de Security Champions
Un modelo organizacional efectivo es el de Security Champions: designar uno o dos desarrolladores por equipo que reciban formación avanzada en seguridad y actúen como embajadores del equipo de seguridad dentro de los equipos de desarrollo. No son expertos en seguridad — son desarrolladores con conocimientos de seguridad suficientes para resolver el 80% de los problemas cotidianos sin escalar al equipo centralizado de seguridad.
Los beneficios: la seguridad escala con los equipos de desarrollo, los problemas se resuelven más rápido porque el security champion conoce el contexto del código, y se crea una cultura de seguridad distribuida en lugar de un cuello de botella centralizado.
Resumen / Summary
- Shift-Left significa mover la seguridad a las fases más tempranas del desarrollo; corregir un bug en diseño cuesta $80 vs $7,600 en producción — la diferencia justifica cualquier inversión en seguridad temprana.
- El Secure SDLC integra seguridad en todas las fases: requisitos de seguridad explícitos, modelado de amenazas STRIDE en diseño, prácticas de código seguro en implementación, y SAST/DAST/SCA en pruebas.
- STRIDE modela 6 categorías de amenazas: Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege — aplicadas sobre diagramas de flujo de datos del sistema.
- Las cuatro prácticas de código seguro más importantes son: validación de input en servidor, codificación de output para prevenir XSS, consultas parametrizadas para prevenir SQL injection, y gestión de secretos con variables de entorno o Vault (nunca hardcodeados).
- SAST (SonarQube, Semgrep) analiza el código estáticamente; DAST (OWASP ZAP) prueba la app en ejecución; SCA (Snyk) verifica vulnerabilidades en dependencias — los tres deben estar integrados en el pipeline CI/CD.
- Los security gates en CI/CD bloquean automáticamente despliegues cuando se detectan vulnerabilidades críticas; Checkov escanea IaC y Trivy escanea imágenes Docker antes de cada despliegue.