miércoles, 6 de mayo de 2009

Inyección SQL

Inyección SQLInyección SQL es una vulnerabilidad informática en el nivel de la validación de las entradas a la base de datos de una aplicación. El origen es el filtrado incorrecto de las variables utilizadas en las partes del programa con código SQL. Es, de hecho, un error de una clase más general de vulnerabilidades que puede ocurrir en cualquier lenguaje de programación o de script que esté incrustado dentro de otro.Una inyección SQL sucede cuando se inserta o "inyecta" un código SQL "invasor" dentro de otro código SQL para alterar su funcionamiento normal, y hacer que se ejecute maliciosamente el código "invasor" en la base de datos.
La inyección SQL es un problema de seguridad informática que debe ser tomado en cuenta por el programador para prevenirlo. Un programa hecho con descuido, displicencia, o con ignorancia sobre el problema, podrá ser vulnerable y la seguridad del sistema puede quedar ciertamente comprometida. Esto puede suceder tanto en programas ejecutándose en computadores de escritorio, como en páginas Web, ya que éstas pueden funcionar mediante programas ejecutándose en el servidor que las aloja.
La vulnerabilidad puede ocurrir cuando un programa "arma" descuidadamente una sentencia SQL, con parámetros dados por el usuario, para luego hacer una consulta a una base de datos. Dentro de los parámetros dados por el usuario podría venir el código SQL inyectado.
Al ejecutarse esa consulta por la base de datos, el código SQL inyectado también se ejecutará y podría hacer un sinnúmero de cosas, como insertar registros, modificar o eliminar datos, autorizar accesos e, incluso, ejecutar código malicioso en el computador.
Asumiendo que el siguiente código está en una aplicación web y que existe un parámetro "nombreUsuario" que contiene el nombre de usuario que nosotros le demos, la inyección SQL es posible:

consulta := "SELECT * FROM usuarios WHERE nombre = '" + nombreUsuario + "';"

Si el usuario escribe su nombre, digamos "Alicia", nada anormal sucedería, la aplicación generaría una sentencia SQL similar a la siguiente, que es perfectamente correcta, en donde se seleccionaría al usuario "Alicia":

SELECT * FROM usuarios WHERE nombre = 'Alicia';

Pero si un usuario malintencionado escribe como nombre de usuario:
"Alicia'; DROP TABLE usuarios; SELECT * FROM datos WHERE '-' = '-", se generaría la siguiente consulta SQL, (el color verde es lo que pretende el programador, el azul es el dato, y el rojo, el código SQL inyectado):

SELECT * FROM usuarios WHERE nombre = 'Alicia';DROP TABLE usuarios;SELECT * FROM datos WHERE '-' = '-';

Protegiéndonos.

Existen ciertos principios a considerar para proteger nuestras aplicaciones de un SQL Injection:
1.) No confiar en la entrada del usuario.
2.) No utilizar sentencias SQL construidas dinámicamente.
3.) No utilizar cuentas con privilegios administrativos.
4.) No proporcionar mayor información de la necesaria.
A continuación veremos algunos ejemplos de como implementar dichos principios.
1. No confiar en la entrada del usuario significa:
Filtrar la entrada del usuario de caracteres SQL para limitar los caracteres involucrados en un SQL Injection.

Private Function string SafeSqlLiteral( _ByVal inputSQL As String) As StringReturn inputSQL.Replace("'", "''")

End Function

'...

Dim safeSQL As String = SafeSqlLiteral(Login.Text)
Dim myCommand As SqlDataAdapter = _New SqlDataAdapter("SELECT au_lname, au_fname " & _"FROM authors WHERE au_id = '" & safeSQL & "'", _myConnection)

· Proteger las instrucciones de búsqueda de modelos coincidentes (LIKE).

Private Function SafeSqlLikeClauseLiteral( _ByVal inputSQL As String) As String
Dim s As String = inputSQL
s = inputSQL.Replace("'", "''")
s = s.Replace("[", "[[]")
s = s.Replace("%", "[%]")
s = s.Replace("_", "[_]")
Return s
End Function

Verificar tanto el tamaño y como el tipo de los datos de las entradas de usuario:
- Evitando los siguientes tipos caracteres de riesgo para el gestor de datos:

-El delimitador de consultas: Punto y coma (;)
-Delimitador de datos de tipo cadena de caracteres: Comilla sencilla (').
-Delimitadores cometario: Guión doble (--) y "/*..*/" en el caso de SQL Server

Nota: En lugar de evitar los caracteres peligrosos, otro modo de protegernos es aceptar sólo los caracteres inofensivos.


+Evitando las cadenas con el inicio de nombres de las tablas y los procedimientos del sistema: "sys" y "xp_" en el caso de SQL Server. Así como las siguientes palabras: AUX, CLOCK$, COM1, COM8, CON, CONFIG$, LPT1, LPT8, NUL y PRN

+Utilizar de preferencia controles con valores predefinidos o discretos tales como cuadros de lista, cuadros combinados, de verificación, etc. en lugar de cuadros de texto.

-Verificar cualquier tipo de entrada, no sólo lo introducido en los controles IU sino también aquellas que no son visibles, como parámetros de entrada y campos tipo hidden de las páginas web.

-Realizar la verificación en todos los niveles y capas de la aplicación, ya que si sólo protegemos la capa de presentación somos vulnerables a que un atacante salte a la siguiente capa y realizar su ataque.

2. No utilizar sentencias SQL construidas dinámicamente. En lugar de ello:
-Utilizar instrucciones SQL con Parámetros

Dim myCommand As SqlDataAdapter =
New SqlDataAdapter( _"SELECT au_lname, au_fname FROM Authors " & _"WHERE au_id= @au_id", cn)
Dim parm As SqlParameter = _myCommand.SelectCommand.Parameters.Add("@au_id", _SqlDbType.VarChar, 11)parm.Value = Login.Text

Aunque de hecho es mejor utilizar Procedimientos Almacenados siempre que sea posible, así como también:

-Utilizar Parámetros al llamar Procedimientos Almacenados.

Dim myCommand As SqlDataAdapter =
_New SqlDataAdapter("AuthorLogin", myConnection)myCommand.SelectCommand.CommandType = _CommandType.StoredProcedureDim parm As SqlParameter = _myCommand.SelectCommand.Parameters.Add("@LoginId", _SqlDbType.VarChar,11)parm.Value = Login.Text

3. No utilizar cuentas con privilegios administrativos.
-Ejecutar las sentencias SQL o invocar Procedimientos Almacenados con una cuenta con privilegios mínimos. Nunca emplear 'sa' en el caso de MS SQL Server. Pero de preferencia además...

-Conceder permisos de ejecución únicamente a Procedimientos Almacenados propios desde los cuales, a manera de "wraper", se realicen las consultas a las Tablas de Usuario y llamados a los Procedimientos Almacenados del Sistema que se requieran en las aplicaciones, y negar el acceso directo a éstos últimos y a las Tablas de Usuario.

4. No proporcionar mayor información de la necesaria.

-No exponer al usuario final los mensajes de error devueltos por el gestor de la base de datos, para no brindar mayor información que sea útil al atacante.

-Implementar un sistema de gestión de errores que notifique del mismo únicamente a los administradores de la aplicación y el gestor de la base de datos. Por ejemplo, para el caso de una aplicación web, establecer en el archivo de configuración web (Web.Config) el valor del atributo debug del elemento compilation en False y el atributo mode del elemento customErrors en On o en su defecto en RemoteOnly.




Otros Ejemplos
http://www.mikesdotnetting.com/Article/113/Preventing-SQL-Injection-in-ASP.NET