Verificando lo que introduce el usuario

Estoy convencido de que el 90% de los problemas de seguridad son debidos a no verificar los datos que introduce el usuario. El otro 10% es debido a configuración indebida. Nunca debemos asumir que el usuario va a introducir los datos correctos, o que no va a poder introducir datos modificados de tal forma que provoquen un fallo en la aplicación.

El siguiente texto muestra ejemplos de programacion en VBScript corriendo en IIS y acceso a bases de datos de Microsoft®.

Supongamos una aplicación web que se conecta a una base de datos y nos muestra una información determinada, por ejemplo, una noticia. Suele ser común numerar las noticias empezando por el numero 1 en incrementos de una unidad. Por ejemplo, una URL del tipo http://servidor/noticia.asp?ID=1 nos mostraría la noticia número 1.

¿Pero que pasa si el usuario introduce un número que no esta en la base de datos? ¿Que pasaría si el usuario construye una URL del tipo http://servidor/noticia.asp?ID=0? Si la aplicación esta bien programada, en vez de un error en tiempo de ejecución, debería comprobar que la noticia existe, y si no es asi mostrarnos un mensaje de error: «Noticia no encontrada en la base de datos».

Y ¿que pasaría si el usuario solicita algo parecido a http://servidor/noticia.asp?ID=Z? El resultado sería aun peor. Ya no solo no solicitamos un número dentro del rango, si no que además, no es un número si no una letra.

Esto que parece trivial, no lo es tanto, y de hecho, es común encontrarnos con este tipo de problemas todos los días. Lo que es quizá mucho peor, es que en muchas situaciones podemos aprovecharnos de estos deslices para sacar partido y hacernos con el control del servidor que esta ejecutando la aplicación. Durante la ultima semana he estado investigando en distintos portales y sitios de noticias donde no se hace ningún tipo de verificación en el servidor sobre los datos que envía el usuario

Un ejemplo real. Supongamos una aplicación que verifica en una base de datos el password de un usuario. Para ello, la aplicación usa una sentencia SQL que podría ser similar a la que sigue:

strSQL = "SELECT * FROM tblUsers WHERE strUsername = '" & strU & "' AND strPassword = '" & strP & "'

Las variables strU y strP contienen respectivamente el nombre de usuario y el password que ha introducido el usuario. Supongamos que el usuario introduce God como nombre de usuario y Jesus como password, la sentencia SQL quedaría transformada en:

strSQL = "SELECT * FROM tblUsers WHERE strUsername = 'God' AND strPassword = 'Jesus'"

La sentencia SQL es correcta. Las columnas strUsername y strPassword son columnas de texto, por eso God y Jesus están entre comillas simples (‘). Pero que pasaría si el usuario introduce como nombre de usuario God y como password ‘ OR strPassword <> ‘password’ OR strPassword = ‘. Entonces la sentencia quedaría:

strSQL = "SELECT * FROM tblUsers WHERE strUsername = 'God' AND strPassword = '' OR strPassword <> 'password' OR strPassword = ''"

La sentencia SQL sigue siendo correcta, pero el usuario ha conseguido modificarla de tal forma que va a devolver un resultado que no es el esperado, engañando a la aplicación web haciéndole pensar que el usuario introdujo el nombre de usuario y password correctos.

¿Cual es el problema y como se soluciona? El problema es como se gestionan las comillas simples. En las instrucciones SQL las comillas simples actúan como delimitadores de las cadenas de texto. Podemos solucionar el problema filtrando las comillas simples que introduce el usuario, bien eliminándolas o bien convirtiéndolas en dos comillas simples. Por ejemplo:

strU = Replace(strU, "'", "")
strU = Replace(strU, "'", "''")

Una función que podemos incluir en todas las páginas ASP que hacen consultas a bases de datos es:

Function FixQuotes(TheString)
FixQuotes = Replace(TheString, "'", "''")
End Function

De este modo, una simple modificación a nuestras cadenas SQL nos quitará muchos dolores de cabeza:

strSQL = "SELECT * FROM tblUsers WHERE strUsername = '" & FixQuotes(strU) & "' AND strPassword = '" & FixQuotes(strP) & "'"

El filtrado de comillas simples no es lo único que debemos tener en cuenta a la hora de validar los datos que introduce el usuario. Si la aplicación espera un número, debemos comprobar que el usuario ha introducido un número, si esperamos un fecha, más de lo mismo. La validación siempre se debe de hacer en el servidor, pues nunca podemos estar seguros que métodos va a usar el usuario para enviarnos los datos. Y por supuesto, una doble validación en el cliente y en el servidor, siempre es lo recomendable, puesto que elimina al servidor de cierta carga.

Funciones como IsDate(), IsEmpty(), IsNull() e IsNumeric() pueden ayudarnos en nuestras tareas diarias cuando programamos.