A lo largo de los últimos meses, hemos sido testigos del crecimiento en la tendencia para la generación de programas de concientización sobre el desarrollo seguro de aplicaciones. En esta décima edición de SEGURINFO en Argentina, esto se ha visto reflejado en numerosas exposiciones enfocadas a dar soporte a esta temática.
Entre ellas, encontramos la charla de Ivan Arce, director del Programa de Seguridad en TIC para la Fundación Sadosky, quien presentó cómo evitar los 10 problemas de seguridad más frecuentes en el diseño de software.
La seguridad en los productos software constituye una propiedad emergente dictaminada por la cohesión de múltiples factores a lo largo del proceso de desarrollo, desde su misma concepción hasta la muerte del producto. Cuando hablamos de evaluar la seguridad de los programas informáticos, hacemos referencia a un conjunto de actividades a lo largo del ciclo de desarrollo que nace que con la idealización del sistema, y se extiende sobre el diseño, la codificación y el fortalecimiento del mismo.
No debemos caer en el error de confundir la seguridad del sistema con las características de éste, como ser la utilización de ciertos protocolos como SSL. Tampoco debemos confundirla con los componentes de seguridad que se ven inmersos en la arquitectura del sistema, como la presencia de firewalls, o limitarla al cumplimiento de una normativa o certificación en particular.
La seguridad del producto es una propiedad dinámica que varía en el tiempo, y que resulta crítica si consideramos el rol que cubren estas aplicaciones en la sociedad moderna. Surge tras reconocer que existen atacantes, y que estos se encuentran siempre dispuestos a probar cada potencial camino de entrada para lograr el control del sistema, y por tanto fuerzan a las empresas de desarrollo de software a pensar mecanismos de control para resistir estos ataques.
Se estima que en la actualidad, el 50% de las vulnerabilidades en los sistemas tienen su origen en fallas de diseño. Estas últimas se diferencian de las fallas de implementación –comúnmente conocidas como bugs, surgidas en la codificación o prueba- por nacer de manera temprana en el proceso de desarrollo y poseer un impacto tan profundo en el sistema que demanda la reingeniería del mismo. Es por esto mismo que resulta crucial desplegar los recursos necesarios para identificar y arreglar estas fallas de diseño de manera temprana, a fin de reducir el costo que éstas producen al arraigarse al producto que se está creando.
¿Cómo guiamos el desarrollo seguro?
Para ayudar a la evaluación de la madurez de la seguridad en el proceso de desarrollo de software, el expositor presenta una lista de problemáticas comunes al momento de diseñar aplicaciones, que pueden llegar a afectar la seguridad en el producto final. Veamos cuáles son estos consejos para el diseño seguro.
1. Ningún componente es confiable hasta demostrar lo contrario
Un error común en el desarrollo de software es englobar funcionalidad sensible en un ambiente de ejecución sobre el cual no tenemos ningún tipo de control. No es debido suponer que los componentes del sistema son confiables hasta que esto pueda ser demostrado.
Por ejemplo, si tenemos un entorno de cliente-servidor, se deben tomar las precauciones contra posibles clientes adulterados desplegando mecanismos de verificación. Debemos pensar que éste se encuentra en el dominio del usuario, quien no siempre tendrá las mejores intenciones.
2. Delinear mecanismos de autenticación difíciles de eludir
La autenticación es el proceso que nos permite acreditar la identidad del usuario y asignarle un identificador único. El desarrollo de métodos autenticación centralizados que cubran cada posible camino de ingreso es uno de los pilares en la construcción de aplicaciones seguras.
Si se trata de páginas web, debemos pensar qué sitios requerirán el manejo de usuarios autenticados, y cuidar que terceros indebidos no se entrometan en el sistema desde URLs no protegidas. La utilización de múltiples factores de autenticación nos permitirá reforzar el sistema comprobando no sólo lo que el usuario sabe sino, por ejemplo, también lo que éste posee.
3. Autorizar, además de autenticar
La autorización es el proceso que designa si un usuario autenticado puede o no realizar una acción que cambia el estado del sistema. Los procesos de autorización sobre usuarios autenticados deben ser pensados desde el diseño y previenen contra sesiones que han caído en las manos equívocas.
4. Separar datos de instrucciones de control
Este punto es clave cuando se trabaja con código capaz de modificarse a sí mismo, o lenguajes que compilan dicho código en tiempo de ejecución -tales como JavaScript-, donde las mismas instrucciones se reciben como datos. Entonces, se vuelve de suma importancia sanear las entradas que recibe el sistema para evitar que atacantes puedan manipular el flujo de ejecución ingresando datos maliciosos.
5. Validar todos los datos explícitamente
Las entradas al sistema deben evaluarse con una filosofía de lista blanca por sobre lista negra: determinar qué se permitirá, y denegar todo aquello que no se corresponda. Debemos pensar que un atacante interpreta los datos como posibles lenguajes de programación, con la intención de manipular el estado del sistema. Por esto, se torna necesario inspeccionar estos datos de entrada, generando los procedimientos automáticos para llevarlos a formas canónicas bien conocidas.
Además, esta validación de entradas debe darse cercana al momento en que los datos son en efecto utilizados, puesto que el desfasaje entre la validación y la utilización brinda una ventana de oportunidad para la generación de ataques.
Para implementar esto, pueden diseñarse componentes comunes que centralicen validaciones tanto sintácticas –estructurales– como semánticas –de significado–, y sacar provecho de los tipos de datos presentes en el lenguaje de programación sobre el cual se está trabajando.
6. Utilizar criptografía correctamente
La comprensión de las nociones criptográficas que aplican al sistema en desarrollo es necesaria para poder entender qué elementos y qué característica de los mismos se busca proteger, contra qué formas de ataque, y consecuentemente, cuál es la mejor manera de lograr este objetivo.
La creación de propias soluciones criptográficas, como siempre, es una decisión riesgosa que puede derivar en un sistema defectuoso, y por tanto es rotundamente desaconsejada. En cambio, se debe acudir al correcto asesoramiento para encontrar las librerías y herramientas que nos permitan aumentar el costo de ataque para el cibercriminal.
7. Identificar datos sensibles y cómo se los debería gestionar
Resulta complicado proteger nuestra información si no tenemos en claro qué es lo que realmente buscamos cuidar. La definición de los datos cuya protección resulta fundamental para el funcionamiento del sistema es crítica, puesto que a partir de ella podremos comenzar a esbozar los procesos para el diseño de la seguridad desde el mismo comienzo del ciclo de desarrollo, y no como un añadido en las etapas de implementación o despliegue.
La definición de los requerimientos de anonimidad y los metadatos que se manejan dará pie a la toma de decisiones en cuanto a los caminos que hacen a su protección.
8. Considerar siempre a los usuarios del sistema
Un sistema técnicamente perfecto que no cubre las necesidades de los usuarios es un sistema inservible. La seguridad utilizable debe ser una de las metas a alcanzar cuando se plantean los objetivos de seguridad para el sistema. Por un lado, no es prudente transferir al usuario cuestiones de seguridad que pueden resolver los mismos desarrolladores, a fin de evitar la fatiga.
Por otro lado, es necesario mantener una comunicación con el usuario para otorgar cierto grado de transparencia sobre cómo opera el sistema. La configuración por defecto debe ser la configuración segura, siempre.
9. La integración de componentes cambia la superficie de ataque
Las aplicaciones actuales constituyen sistemas complejos con muchos componentes interactuando de manera simultánea. Cada vez que se realiza un cambio en el sistema, el panorama de seguridad cambia y debe ser reevaluado. Esta reexaminación es el resultado de la coordinación entre las áreas y proyectos.
Los componentes deben ser analizados de manera unitaria y en conjunto, teniendo en cuenta cómo se combinan, mantienen o reemplazan.
10. Considerar cambios futuros en objetos y actores
Desde el diseño, debemos considerar que las propiedades del sistema y sus usuarios cambian constantemente. Algunos factores a considerar son el crecimiento de la población de usuarios, cómo las migraciones afectan al sistema, o cómo afectarán vulnerabilidades futuras sobre componentes que se han desplegado a gran escala.
Los procedimientos de actualización de manera segura deben de diseñarse con un horizonte a futuro de meses, años o incluso décadas.
En resumen…
Las organizaciones actuales han comenzado a comprender que la identificación y remediación temprana de problemas posee un costo inversamente proporcional al tiempo que el error permanece en el sistema.
La instauración de un ciclo de desarrollo seguro mediante la instrumentación de un modelo de diseño orientado a la seguridad que genere sinergia entre el área de seguridad y desarrollo, nos acerca un paso más hacia el despliegue de aplicaciones más robustas y mucho más rentables.