Buenas Prácticas en el Desarrollo de Software Seguro
Modelado de Amenazas
El modelado de amenazas es una actividad crítica de la fase de diseño que permite anticipar qué ataques podría enfrentar la aplicación y cómo mitigarlos antes de escribir código.
Puntos clave para integrarlo en el plan de desarrollo:
- Ubicación en los Marcos: En OWASP SAMM, forma parte de la función de “Diseño”. En el NIST SSDF, se clasifica bajo la práctica PW.1.1, que instruye el uso de modelos de riesgo para evaluar la seguridad del software.
- Niveles de Madurez (según SAMM):
- Nivel 1: Realizar un modelado de amenazas básico o limitado para aplicaciones críticas.
- Nivel 2: Estandarizar el proceso en todos los equipos y hacerlo medible.
- Acciones Prácticas:
- Identificar Actores: Definir “agentes de amenaza” (entidades que pueden causar impacto negativo).
- Entrenamiento: Capacitar al equipo de desarrollo o colaborar con expertos para crear los modelos.
- Documentación: Registrar las decisiones de diseño y las respuestas a los riesgos como artefactos para futuras auditorías.
- Enfoque en Datos: Realizar un mapeo de la superficie de ataque y clasificación de datos para identificar áreas de alto riesgo.
Documentación de Hallazgos
Para integrar la documentación de hallazgos en el plan de desarrollo, es importante considerar que estos registros sirven como artefactos de auditoría y guías para el equipo.
Puntos clave para documentarlos según los estándares:
- Dónde registrar: Utiliza sistemas de seguimiento de tickets (como Jira), wikis compartidas o directamente dentro del modelo de amenazas.
- Contenido esencial: Cada hallazgo debe incluir la descripción del riesgo, su severidad (Baja, Media, Alta), el impacto potencial y la recomendación técnica para mitigarlo.
- Evolución por niveles (DSOMM):
- Nivel inicial: Visualizaciones simples o listas de defectos encontrados.
- Nivel gestionado: Dashboards que muestran el backlog de vulnerabilidades y reportes específicos por cada Pull Request (PR).
- Nivel avanzado: Análisis de causa raíz para identificar patrones de debilidad en el código y evitar que se repitan en el futuro.
Una práctica moderna y eficiente es automatizar este registro para que aparezca como comentarios directamente en el código durante la fase de implementación.
Validación de Entradas
La validación de entradas es la primera línea de defensa técnica. Su objetivo es asegurar que la aplicación solo procese datos con el formato, tipo y longitud correctos antes de que lleguen a funciones críticas.
Principios fundamentales para el plan de desarrollo:
- Regla de Oro: Toda validación se realiza en un sistema confiable (lado del servidor). Los controles en el cliente (navegador) son fáciles de omitir con proxies como Burp Suite.
- Estrategia de “Listas Blancas” (Allow-lists): En lugar de intentar filtrar caracteres “malos”, se define exactamente qué caracteres están permitidos. Si el dato no coincide, debe ser rechazado inmediatamente.
- Centralización: Implementar una rutina de validación centralizada para toda la aplicación en lugar de dispersar las comprobaciones en cada formulario.
- Verificaciones Específicas: Validar siempre:
- Tipo de dato: (Ej. ¿Es realmente un número entero?).
- Longitud y Rango: (Ej. ¿El nombre tiene menos de 50 caracteres? ¿La edad está entre 1 y 120?).
- Sintaxis: (Ej. ¿Cumple con el formato de un correo electrónico?).
Si no se valida correctamente, la aplicación queda expuesta a fallos graves como inyecciones SQL o ataques de entidades externas XML (XXE), como se practica en entornos como WebGoat.
Codificación de Salidas
La codificación de salidas es el proceso de convertir datos no confiables en un formato seguro antes de enviarlos al cliente para evitar que el navegador los interprete como comandos activos, mitigando ataques como el Cross-Site Scripting (XSS).
Los principios fundamentales a integrar en el plan de desarrollo son:
- Codificación Contextual: El método de codificación debe variar según el destino de los datos; no es lo mismo mostrar información en el cuerpo de un HTML que dentro de un atributo, en un bloque de JavaScript, en CSS o en una URL.
- Realización en el Servidor: Al igual que la validación, toda codificación debe ejecutarse en un sistema confiable (lado del servidor) antes de devolver los datos al cliente.
- Uso de Rutinas Estándar: Debes emplear bibliotecas y funciones estándar ya probadas para manejar caracteres peligrosos (como
< > " ' % & + \) en lugar de crear métodos propios. - Sanitización de Consultas: Para salidas destinadas a otros intérpretes (como SQL, XML o LDAP1), los datos deben ser sanitizados contextualmente para evitar inyecciones.
Esta práctica es una de las recomendaciones principales del NIST SSDF (PW.5.1) para reducir el número de vulnerabilidades introducidas durante la creación del código fuente.
Cross-Site Scripting (XSS)
La codificación de salida (output encoding) previene los ataques XSS al convertir datos no confiables en un formato “inerte” que el navegador no puede ejecutar como código activo.
Así es como funciona técnicamente para proteger la aplicación:
- Neutralización de caracteres: Sustituye caracteres especiales peligrosos por sus equivalentes en entidades HTML (por ejemplo, convierte
<en<y>en>). Al hacer esto, el navegador muestra el símbolo literalmente en lugar de interpretarlo como el inicio de una etiqueta<script>. - Enfoque Contextual: El método de codificación cambia según dónde se muestren los datos; por ejemplo, se aplican reglas distintas si el dato va dentro de un atributo HTML, en un bloque de JavaScript, en una URL o en CSS.
- Ejecución en el Servidor: Para que sea efectiva, la codificación debe realizarse en un sistema confiable (lado del servidor) antes de que la respuesta llegue al cliente.
Esta práctica asegura que, incluso si un atacante logra inyectar un script en tu base de datos, este se muestre como simple texto sin causar daño al usuario final.
Autenticacion y Gestión de Sesiones
La autenticación y gestión de sesiones garantiza que solo los usuarios legítimos accedan a la aplicación y que su identidad se mantenga protegida durante su visita.
Controles clave para el plan de desarrollo:
- Centralización: Implementar un componente único para verificar la autenticación y las sesiones en toda la aplicación para evitar inconsistencias.
- Fallo Seguro: Los controles de acceso deben “negar por defecto” (deny by default); si el sistema no puede verificar la identidad, el acceso debe ser rechazado inmediatamente.
- Transmisión Protegida: Utilizar únicamente solicitudes HTTP POST para enviar credenciales y asegúrate de que toda la comunicación ocurra sobre TLS (HTTPS).
- Mensajes Genéricos: Evitar dar pistas a atacantes. En lugar de “Usuario incorrecto”, utiliza mensajes genéricos como “Usuario y/o contraseña inválidos”.
- Seguridad de Cookies: Configura el atributo
HttpOnlypara evitar que scripts maliciosos (XSS) accedan a los identificadores de sesión.
Manejo de Errores y Logs
El Manejo de Errores y Logs es vital para evitar fugas de información técnica y asegurar la trazabilidad de incidentes.
Manejo de Errores
- Mensajes Genéricos: No revelar detalles del sistema, identificadores de sesión o información de cuentas en las respuestas de error; utiliza siempre páginas de error personalizadas.
- Fallo Seguro: La lógica de manejo de errores asociada a controles de seguridad debe “negar el acceso por defecto” en caso de falla.
- Sin Depuración: Asegúrar de que los controladores de errores no muestren trazas de la pila (stack traces) o información de depuración al usuario final.
- Gestión de Memoria: Libera adecuadamente la memoria asignada cuando ocurran condiciones de error para evitar fugas de recursos.
Logs (Registro de Eventos)
- Sistema Confiable: Implementar todos los controles de registro en un sistema confiable (como el servidor) y restringir su acceso únicamente a personal autorizado.
- Eventos Críticos a Registrar: Registrar fallas de validación de entrada, intentos de autenticación (especialmente fallidos), fallas de control de acceso y excepciones del sistema.
- Datos Esenciales por Evento: Cada entrada debe incluir marca de tiempo, calificación de severidad, identidad del usuario, IP de origen y descripción del resultado.
- Protección contra Inyección de Logs: Asegúrar de que los datos no confiables registrados no se ejecuten como código en las herramientas de visualización de logs.
- Privacidad: Nunca almacenr información sensible en los registros, como contraseñas, detalles innecesarios del sistema o identificadores de sesión.
Esta sección se alinea con la práctica PW.5.1 del NIST SSDF, que exige proporcionar capacidades de registro y trazabilidad.
- LDAP Lightweight Directory Access Protocol (Protocolo Ligero de Acceso a Directorios): es un protocolo de red estándar de la industria que se utiliza para buscar, gestionar y autenticar información de usuarios y recursos en una red centralizada.
En lugar de crear un sistema de usuarios propio para cada aplicación, el software se conecta a un servidor LDAP (como Active Directory de Microsoft o OpenLDAP) para verificar si un usuario y su contraseña son correctos. ↩︎