Trucos para un PC más seguro: programación con garantías

Además del sistema operativo, clave en la autentificación y control de accesos, la seguridad del software debe atender al diseño de las aplicaciones que actúan sobre él

Trucos para un PC más seguro: programación con garantías

31 enero 2009

Un programa mal diseñado puede representar una brecha de seguridad para un sistema informático desde distintas perspectivas: perjudica su estabilidad, pone en peligro la integridad de los datos con los que opera, facilita vías de entrada a hackers, etc.

En teoría, las imposiciones de seguridad de un proyecto deberían quedar plasmadas desde el inicio del proceso, como el resto de los requerimientos de la aplicación, tanto funcionales como no funcionales. En la práctica, sin embargo, la seguridad no suele ser un aspecto que se tome en demasiada consideración.

De todos es sabido que lo más importante para el éxito de un proyecto es que esté acabado a tiempo y cumpliendo presupuesto, aunque para ello sea preciso sacrificar otros objetivos «menos aparentes» de cara al usuario final.

A pesar de lo dicho, tampoco hay que ser obsesivo con la seguridad siempre que se desarrolle software y, en cualquier caso, el esfuerzo invertido en mejorar este apartado ha de estar siempre en consonancia con los requerimientos del proyecto y los usuarios que lo utilizarán.

No debe exigirse el mismo nivel de seguridad a un programa que se ejecutará en un único computador aislado del resto del mundo y con información no vital, por poner un ejemplo característico en muchas pymes, que a una aplicación que deba atender a miles de usuarios con una alta disponibilidad u otra cuya función sea supervisar constantes vitales para la vida de las personas.

Técnicas de prueba

El primer nivel de seguridad que debe pedirse a cualquier aplicación, sea cual sea su funcionalidad y complejidad, es que realice el trabajo que se supone debe llevar a cabo generando resultados correctos, sin errores. Para garantizar que sea así, el equipo de desarrollo debe someter el software a distintas baterías de pruebas. Éstas tienen más éxito cuanto mayor es el número de fallos detectados, siempre en un estadio que permite su resolución antes de que el proyecto llegue a manos del cliente final.

Las técnicas de prueba que se aplican usualmente a un proyecto software se dividen en dos categorías: pruebas de caja negra y de caja blanca. Las primeras sirven para verificar la funcionalidad del programa sin conocer detalles sobre su implementación, utilizándolo como lo haría un usuario final.

Las pruebas de caja blanca se diseñan precisamente conociendo los detalles sobre cómo se ha efectuado la implementación, siendo su objetivo comprobar todas las vías posibles de ejecución y llevar el programa hasta extremos que no es posible alcanzar con las de caja negra.

El proceso de definición, implementación y ejecución de baterías de pruebas adecuadas conlleva el consumo de una parte importante de los recursos, en tiempo y por tanto en dinero, asignados a cualquier proyecto. En conjunto, sin embargo, representan la mejor garantía de que el software final hará lo que debe hacer con el menor número de errores posible. La mayoría de entornos de desarrollo actuales incorporan opciones para automatizar parte de este trabajo.

Malas y buenas prácticas

La seguridad de las aplicaciones puede mejorarse de forma considerable siguiendo simplemente una serie de prácticas, algunas de ellas casi de sentido común, que persiguen fundamentalmente que el código se ejecute sin generar errores que puedan afectar a su funcionamiento.

Lo primero que debe hacerse es elegir el lenguaje y plataforma adecuados según el tipo de aplicación a desarrollar, algo que puede parecer hasta obvio. El software desarrollado con Java o C#, por ejemplo, tiene estadísticamente muchos menos fallos que el escrito con C o C++.

Algo que no debe extrañar porque estos últimos carecen de recolector de basura, comprobación de límites y los mecanismos de protección del entorno de ejecución de la plataforma Java o .NET. Si no se precisan las características específicas de lenguajes como C y C++, no hay razón alguna para utilizarlos en detrimento de opciones como las mencionadas.

Se debe evitar reinventar la rueda constantemente, ya que esta mala práctica es una fuente habitual de errores en las aplicaciones. Si se precisa un determinado tipo de colección de datos, por poner un ejemplo clásico, debe siempre recurrirse a la ofrecida por la biblioteca del lenguaje o plataforma elegidos, en lugar de implementarla manualmente, ya que es seguro que la primera estará mucho más probada y libre de errores. Esto mismo se aplicaría a cualquier otra funcionalidad que pueda precisar el proyecto y que ya exista.

El código de una aplicación debe mantener una estructura lo más simple posible, aplicando siempre que se precise la conocida técnica del «divide y vencerás». Resulta más difícil tener una visión global y, al tiempo, controlar todos los detalles de un bloque de cientos de líneas de código que, como alternativa, de varios más pequeños, cada uno de los cuales se encarga de una tarea más sencilla. En el primer caso, es mucho más fácil introducir errores y más difícil encontrarlos.

Siempre que se disponga de herramientas de análisis automatizado de código deben ser utilizadas, especialmente con lenguajes del tipo C/C++, ya que con ellas se identificarán riesgos de seguridad, tales como el acceso a bloques de memoria que se han liberado previamente, competencia por recursos entre distintos hilos de ejecución, interbloqueos, etc.

Errores y hackers

Proteger completamente el software contra todo ataque es imposible. Cuanto más se mejora la seguridad, tanto más ingenio aplican los hackers, pero con algunas comprobaciones básicas puede reducirse la probabilidad de éxito de algunos de los ataques más comunes.

Dos de las técnicas a las que más recurren estas personas son, en primer lugar, la inyección de código. A esta categoría pertenece, por ejemplo, la denominada SQL Injection, consistente en forzar la ejecución de código en un servidor de datos para extraer información a la que normalmente no se tendría acceso.

En segunda instancia, la técnica de desbordamiento de búfer es más peligrosa si cabe, ya que a través de ella el atacante puede llevar a ejecutar código arbitrario sobre el sistema operativo.

Cualquier programa que procese parámetros recogidos desde el exterior, ya sean argumentos introducidos por el usuario desde una línea de comandos, datos recogidos en un formulario web o interpretación de una URL, debe comenzar verificando que la longitud de esos parámetros no excede la capacidad de las zonas de memoria donde vayan a ser almacenados, evitando así el desbordamiento.

A continuación, y antes de usar dichos parámetros, se debería comprobar que su contenido no resulta peligroso. Dependiendo del tipo de procesamiento que vaya a efectuarse con ellos, no deberían contener comillas (simples o dobles), asteriscos y otros símbolos que tengan un significado especial para el gestor de bases de datos o intérprete de comandos.

En cualquier caso, salvo excepciones, la aplicación debería ejecutarse por norma con el nivel más bajo de privilegios que sea posible, nunca con credenciales de administrador o superusuario. De esta forma, resultará más difícil que un fallo en la misma pueda afectar globalmente a la seguridad del sistema.