| 9 min de lectura
Contenido
Nunca cesa de ser evidente lo inseguro que tiende a ser el software utilizado por personas y organizaciones en casi todas las industrias del planeta. Basta con echar un vistazo a las noticias de The Record del último mes para encontrar ciberataques dirigidos a los sectores de aviación, espacio y educación (como vimos recientemente, este último es uno de los más atractivos para los actores de amenazas actualmente). Si bien es cierto que muchos ataques suelen tener éxito gracias a la ingeniería social (p. ej., campañas de phishing), muchos otros logran su objetivo a través de otro importante vector de ataque: la explotación de vulnerabilidades de seguridad en el software.
Aquí no estamos hablando algo que afecte únicamente a los débiles y pequeños. Las grandes empresas que proporcionan software como producto o servicio (ejemplos recientes: Google, Siemens, y Microsoft), así como organizaciones bien conocidas que hacen uso de software (ejemplos recientes: una agencia federal, Aurubis, y Mitel), siguen viéndose gravemente afectadas por los ciberataques. Muchas vulnerabilidades explotadas por hackers maliciosos para robar información o interrumpir operaciones se encuentran en el código fuente de las aplicaciones (es decir, aquellas instrucciones que definen su estructura y funcionalidad y que son interpretadas por los dispositivos). Por eso, una de las mejores formas de prevenir los ciberataques y sus repercusiones es desarrollar correctamente los productos de software, escribiendo un código fuente seguro desde el principio del ciclo de vida de desarrollo de software (SDLC, por su nombre en inglés).
¿Qué es la codificación segura?
La codificación segura o programación segura es la práctica de desarrollo de software en la que se evita la aparición de errores de programación que den lugar a vulnerabilidades de seguridad. Esta actividad implica un alto conocimiento del lenguaje de programación en uso y el seguimiento juicioso de convenciones y principios o estándares de codificación segura. Es por ello que los equipos de desarrollo de cualquier empresa interesada en su ciberseguridad deben estar capacitados al respecto.
Resulta problemático que muchos desarrolladores vean como un obstáculo la inclusión de principios de seguridad en el desarrollo de software. Una de las causas es la continua demanda de rápida salida de los productos y de nuevas funcionalidades en ellos. En su prisa por responder rápidamente a las peticiones de clientes o usuarios, a veces dirigidos por directivos apáticos a una cultura de ciberseguridad, los desarrolladores pasan por alto la exposición al riesgo. En otros casos, puede que se les pida o se les obligue a prestar atención a la seguridad, pero factores simples como la falta de concentración o el desconocimiento también pueden impedir una codificación segura. La falta de conocimientos sobre ciberseguridad de los desarrolladores de software es algo habitual hoy en día, hasta el punto de que pueden ignorar, por ejemplo, la existencia de estándares públicos como OWASP. En realidad, un primer paso para prevenir las vulnerabilidades de seguridad puede ser disponer de, y mantener bajo revisión, bases de datos de vulnerabilidades estandarizadas como CWE, CERT, CVE, OWASP y PA-DSS, entre otras. (Consulta también nuestro listado de tipos de vulnerabilidades.)
Las organizaciones que desarrollan y ofrecen software como producto o servicio, y aún no lo han hecho, deben empezar a crear o reforzar la cultura basada en la seguridad dentro de sus unidades. Aquí es donde entra en juego el famoso enfoque DevSecOps según el cual la seguridad debería ser responsabilidad de todos los miembros del equipo y no solo algo para un equipo de seguridad. Aunque puede ser un cambio complejo y lento, es un esfuerzo que vale la pena en términos de costos y posibilidades de éxito, y en el que pueden intervenir los llamados "campeones de seguridad" (de los que ya hemos hablado.) Como veremos más adelante, el trabajo de los desarrolladores puede verse considerablemente facilitado por las pruebas de seguridad. Sin embargo, sigue siendo esencial que entiendan lo que están haciendo en términos de riesgos y amenazas, que sepan dónde están cometiendo errores y que empiecen a mantener prácticas que les permitan evitar estas fallas y, por consiguiente, la aparición de vulnerabilidades en sus productos.
Algunas prácticas de codificación segura
Es fácil encontrar en la Internet buenas prácticas o guías de codificación segura (p. ej., OWASP DevGuide, Microsoft Writing Secure Code, Red Hat Secure Coding Tutorials). Para este artículo, hemos tomado en parte como base la Secure Coding Practices - Quick Reference Guide (te recomendamos que la consultes para más detalles) y hemos añadido otras consideraciones y algunos consejos valiosos que vale la pena mencionar. (Consulta también nuestra serie de requisitos de seguridad.)
Seguridad desde la primera línea de código
Es imprescindible partir de la idea de no dejar la seguridad para el final del SDLC. Pensar y actuar a favor de un producto de software seguro desde la primera línea de código permite evitar elevados costos posteriores. No solo los costos en la remediación de vulnerabilidades (estos son mucho menores en las fases de desarrollo que en las finales o en producción) sino también los derivados de su explotación, en otras palabras, de las brechas de seguridad. Los desarrolladores no deberían tener como único objetivo la rápida puesta en producción de un producto con una funcionalidad óptima. Entre sus principales propósitos, deberían incluir la salida a producción de un software de alta calidad que garantice la seguridad.
Pensar como actores de amenazas
Los desarrolladores deberían intentar ver sus creaciones como si ellos mismos fueran hackers maliciosos. (Ya escribimos una vez un artículo titulado ¡Piensa como un hacker!, el cual puede servir como referencia al respecto). No solo deben centrarse en los fines y casos de uso del producto, sino también en cómo podrían explotarlo los delincuentes en caso de tener vulnerabilidades. Los desarrolladores deberían tener claro qué activos y operaciones serían atractivos para los actores de amenazas. Deberían ser conscientes de las amenazas potenciales y de los niveles de riesgo y tener en cuenta y poner en práctica medidas preventivas.
Validación de inputs o entradas
El simple uso de la aplicación o el software desarrollado, concretamente la introducción de datos, suele representar un riesgo. Por ejemplo, los ataques conocidos como inyección SQL y cross-site scripting (XSS) pueden producirse debido a vulnerabilidades derivadas de confiar en fuentes de datos externas y en el input del usuario, por lo que el software no distingue entre comandos y datos. En otras palabras, ciertos caracteres pueden entrar en la aplicación funcionando como código malicioso y hacer que esta se comporte de forma anormal o desviándose de su funcionamiento previsto. De ahí la necesidad de validar continuamente lo que entra en el producto informático.
Los desarrolladores deben asegurarse de que las fuentes de datos sean clasificadas como confiables y no confiables. Su producto debe validar adecuadamente el input (principalmente) procedente de fuentes no confiables. Debe verificar las propiedades de los datos entrantes y aceptar solo los inputs que cumplan unas características específicas (p. ej., tipo, rango, longitud, caracteres permitidos). Si los inputs no las cumplen, la aplicación debe rechazarlos. A este proceso, los desarrolladores deberían añadir la codificación de salida, o output, en la que todo input que no sea de confianza se transforme a una forma segura, permanezca como dato y no se ejecute como código.
Autenticación y gestión de contraseñas
El software debe verificar mediante un proceso estandarizado la identidad del usuario o entidad que interactúa con él, especialmente cuando se intenta acceder a recursos que no están destinados a ser públicos o a sistemas externos con material confidencial. Las fallas de autenticación deberían generar respuestas que no especifiquen cuáles de los datos solicitados eran erróneos o inválidos. Las operaciones críticas, como las transferencias de dinero, por ejemplo, deberían solicitar una nueva autenticación o una autenticación multifactor.
Si la aplicación almacena credenciales, los desarrolladores deberían asegurarse de que siempre sean hashes sólidos de contraseñas criptográficamente unidireccionales. Para reforzar la complejidad de las contraseñas o passphrases (preferiblemente frases de contraseña), la aplicación debería exigir a los usuarios que incluyan en ellas números y caracteres especiales, además de letras minúsculas y mayúsculas. Tras varios intentos fallidos de inicio de sesión por parte de un usuario, el software debería desactivar esa cuenta durante un periodo determinado. Además, la modificación de la contraseña debería controlarse adecuadamente, y la aplicación debería notificar al usuario cada vez que ocurra.
Control de acceso y gestión de sesiones
Además de la verificación de la identidad del usuario, el producto debe contar con un proceso que permita o deniegue el acceso a los recursos. Los desarrolladores deberían restringir el acceso a determinados recursos (incluidas URLs, funciones, servicios, archivos y datos críticos protegidos) solo a unos pocos usuarios autorizados. Siempre deberían aplicar el principio de privilegio mínimo. Por defecto, el acceso debe ser denegado, y el sistema debe mantener y verificar las condiciones o características (más allá de la verificación de roles) para permitirlo. Lo ideal es que el programa restrinja a los usuarios el acceso únicamente a los recursos necesarios para realizar sus tareas o trabajos. En un periodo determinado, debería limitarse el número de transacciones de un usuario o entidad y deben eliminarse las cuentas no utilizadas. Además, los desarrolladores deberían establecer tiempos de inactividad de sesión lo más cortos posible, no permitir inicios de sesión simultáneos con la misma cuenta y generar nuevos identificadores de sesión para sustituir periódicamente a los previamente usados.
Criptografía y protección de datos
La protección o confidencialidad de los datos depende en gran medida del uso de algoritmos de cifrado bien conocidos, probados y actualizados para datos sensibles en tránsito y en reposo. En línea con lo mencionado anteriormente para las contraseñas, estas y ninguna otra información sensible debería almacenarse en texto claro o de cualquier otra forma que no implique criptografía. En el código que pueda ser accesible a los usuarios (no se debería permitir que los usuarios descarguen el código fuente del servidor), los desarrolladores deben eliminar los comentarios que revelen información confidencial. Lo mismo debería hacerse con la documentación innecesaria sobre la aplicación. El producto de software también debe permitir eliminar los datos confidenciales cuando ya no sean útiles. Este mismo tipo de información no debería estar presente en las cookies, y el manejo de tales datos no debería conducir a la generación de copias en caché.
Gestión de errores y registro
En relación con lo que hemos mencionado para los errores de autenticación, las actividades no válidas en la aplicación o el producto pueden generar mensajes de error. La idea es que estos mensajes no revelen información que pueda ser útil para posibles atacantes (p. ej., identificadores de sesión, detalles del sistema o información de la cuenta). Esta misma información no debería almacenarse en los registros. El registro de los eventos que se producen en el código permite identificar errores. Las acciones que provocan fallas en la aplicación (p. ej., validación de input, autenticación, control de acceso, funciones administrativas, módulos criptográficos) deberían ser registradas y bloqueadas. Los desarrolladores deberían restringir el acceso a todos los registros únicamente a un grupo de usuarios autorizados.
Configuración y control de sistema
Los desarrolladores deben asegurarse de que los servidores, marcos de trabajo y otros componentes del sistema estén en sus últimas versiones verificadas con todos los parches de seguridad pertinentes aplicados. Además, deberían eliminarse todos los archivos y componentes de aplicación innecesarios (p. ej., porciones de código de terceros). Esto guarda cierta relación con la idea de mantener el código y los sistemas lo más limpios y sencillos posible. Al reducir la complejidad del producto, incluyendo solo lo realmente necesario, los desarrolladores reducen la probabilidad de que surjan vulnerabilidades de seguridad. Adicionalmente, se recomienda que mantengan el uso de sistemas de control de código fuente y un seguimiento minucioso de los cambios.
Revisión de código fuente
Las anteriores son solo algunas de las prácticas recomendadas para la codificación segura. Existe para ellas un complemento casi indispensable que permite a los desarrolladores garantizar la alta calidad de sus productos de software. Se trata de las pruebas de seguridad, especialmente en modo de revisión de código fuente o secure code review. En una entrada reciente del blog, describimos este tipo de prueba y los beneficios que puede aportar a una organización que desarrolla software. Básicamente, se trata de una contribución de un proveedor externo, como Fluid Attacks, en la que herramientas automatizadas y humanos (algunos proveedores se limitan erróneamente al uso de herramientas) tienen la misión de detectar vulnerabilidades de seguridad en el código fuente.
Aunque los grupos de desarrolladores pueden educarse a sí mismos o recibir formación sobre prácticas como las descritas anteriormente, no es extraño que sigan apareciendo fallas y vulnerabilidades en su trabajo con el paso del tiempo. A través de la revisión de código fuente de Fluid Attacks, en la que participan nuestras herramientas y nuestro equipo de hackers éticos con técnicas como SAST y SCA, y en la que damos soporte a unos 40 lenguajes de programación y utilizamos más de 60 estándares internacionales de seguridad como base para la evaluación, informamos a tus equipos de desarrollo de los problemas de seguridad que surgen en sus construcciones. Como ya hemos dicho, estos informes les sirven de retroalimentación, y la práctica de remediar las vulnerabilidades identificadas alimenta sus conocimientos sobre codificación segura. Aunque no dejarán de producirse errores, podrán advertirlos y solucionarlos más fácilmente.
Como el software tiende a evolucionar a un ritmo increíble para satisfacer las necesidades de los usuarios, ofrecemos la revisión fuente como parte de nuestro servicio Hacking Continuo. En este proceso constante, estamos atentos a todos los cambios en tus repositorios, y añadimos técnicas más allá de la revisión de código fuente, como las pruebas de seguridad de aplicaciones dinámicas (DAST, por su nombre en inglés). Contáctanos si quieres descubrir qué están pasando por alto tus equipos de desarrollo y qué está poniendo en riesgo a tu organización. Regístrate aquí si quieres empezar con una prueba gratuita de 21 días de pruebas de seguridad continuas mediante nuestras herramientas automatizadas.
Contenido
Comparte
Blog posts recomendados
Quizá te interesen los siguientes posts similares.
Retos, amenazas y buenas prácticas para los comerciantes
Sé más seguro aumentando la confianza en tu software
En qué consiste y cómo mejora tu postura de seguridad
Ataques complejos basados en la web y medidas proactivas
La importancia de las API seguras en este mundo dominado por apps
Protege tus aplicaciones en la nube de las ciberamenazas
Detalles de esta tendencia y dudas sobre la privacidad de los datos