La vulnerabilidad de una inyeccion SQL surge cuando el programador declara queries dinamicas en la logica de su base de Datos. Aqui hay un ejemplo en PHP de esta vulnerabilidad:
Código
$offset = $_GET['offset']; $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
Estas queries son creadas por la concatenacion de strings que el usuario define. Para evitar esta vulnerabilidad El programador tiene dos opciones:
- Dejar de usar queries dinamicas.
- Evitar que el SQL malicioso del usuario afecte la logica de la base de Datos.
Aqui pretendo tratar unas medidas simples que los programadores pueden tomar para evitar o en todo caso reducir el daño de una iSQL. Las tecnicas incluidas aqui son casos generales, y tambien pueden ser aplicadas para defenderse de toros tipos de ataques similares como las inyecciones XPath y XQuery por ejemplo.
La ley del privilegio
Por lo general en la seguridad informatica es Buena idea que las aplicaciones se ejecuten con privilegios bajos, solo con lo necesario. Esto es especialmente verdad cuando se trata de una aplicacion que sera usado remotamente por usuarios en los que no se confia (por lo general es mala idea confiar en el usuario de tu aplicacion). Por eso quiero empezar con este tema, sirve como una Buena practica y mitiga El daño incluso despues de una explotacion exitosa.
La mejor manera para ejecutar esto es tener distintos usuarios de privilegio limitado en tu entorno. El error comun aqui es que muchos programadores toman El Camino facil de darle DBA o admin de base de Datos a las cuentas de su aplicacion. Esto es peligroso y aumenta el daño de una iSQL existosa. Esta bien que todo "funciona Bien" cuando lo haces pero si una aplicacion es comprometida el resto de la base de Datos queda expuesta.
La implementacion de esta medida varia segun la plataforma que estas usando pero la idea es la misma. Empieza de abajo hacia arriba, no es recomendable darle todos los privilegios a un usuario de base de Datos e irle quitando los que no necesita. Puedes pasar por alto un privilegio sin darte cuenta pero el 1337 hacker que quiere tus Datos si lo identificara. Es por ESO que debes tomar en cuenta que es lo que necesita tu cuenta y darle solo eso. Una cuenta que solo lee Datos de una tabla solo require privilegios de lectura y solo para la tabla en cuestion. Por ejemplo, en una e-commerce una cuenta que solo arroja productos con su descripcion solo deberias de tener permisos de lectura solo para la tabla de productos y nada mas. El darle otros privilegios Seria una amenaza de seguridad. Si una cuenta solo require partes de una tabla, crea una view que se los da. Y a esta cuenta dale permiso solo para esa view y no para la tabla que hay detras.
Usando queries preparadas
Esta es la forma en que se deberia de enseñar a los programadores a hacer sus queries. En vez de usar un string para la queries, Este las toma en la forma de un parametro. Esto es Algo similar a como algunos lenguajes como perl manejan su funciona de system() precisamente para evitar una inyeccion. Este estilo le permite a tu base de Datos distinguir entre Datos y codigo. Ademas de que es mas facil de leer y te obliga a primero programar tu base de datos y despues pasar los parametros a la formacion de queries. Por ejemplo, si un hacker intenta buscar "juan' or '1'='1" en vez de ser vulnerable a que se ejecuten como codigo, la base de Datos buscara un usuario que se llame "juan' '1'='1". Aqui una's maneras de hacerlo segun El lenguaje que uses:
- JAVA EE: se usa PreparedStatement() con variables enlazadas
- .NET: Usando queries parameterizadas como SqlCommand() o OleDbCommand().
- PHP: Usa PDO con queries estrocamente declaradas con bindParam().
[li]SQLite: Usa sqlite3_prepare() para crear un objeto de query.[/li]
[/list]
JAVA:
Código
// Esto tambien deberia ser validado pstmt.setString( 1, nombreCliente);
C# .NET:
En .net la creacion y ejecucion de una query no cambian, solo pasa los parametros Al query usando Parameters.Add().
Código
String query = "SELECT saldo_cuenta FROM datos_usuario WHERE nombre_usuario = ?"; try { OleDbCommand command = new OleDbCommand(query, connection); command.Parameters.Add(new OleDbParameter("nombreCliente", CustomerName Name.Text)); OleDbDataReader reader = command.ExecuteReader(); // … } catch (OleDbException se) { // En Caso de error }
Practicamente cualquier lenguaje de programacion tiene manera de hacer esto (para mas ejemplos buscar OWASP parameterized query cheat sheet). Incluso las abstracciones de SQL tambien tienen esta capacidad como por ejemplo HQL antes mencionado:
Código
//HQL no seguro usando concatenacion de strings Query HQLNoSeguro = SESSION.createQuery("from inventario where productoID='"+parametroUsuario+"'"); //La misma pero en su forma Segura usando parametros nombrados Query HQLSeguro = SESSION.createQuery("from Inventorio where productoID=:productoid"); HQLSeguro.setParameter("productoid", parametroUsuario);
Una de Las ventajas de usar queries preparadas es que la logica se queda dentro de la aplicacion haciendola independiente de la base de datos.