Anda di halaman 1dari 4

Cmo evitar SQL Injection + Prepared Statements en PHP Luego de escribir la serie de artculos sobre hacking usando SQL

Injection, creo que es tiempo de escribir sobre algn mtodo de prevencin >=) Como se puede observar en cualquier bibliografa "decente" sobre SQL Injection, existen bsicamente 3 formas de prevenir la inyeccin, las cuales estn muy bien descriptas en el artculo de OWASP SQL Injection Prevention Cheat Sheet. Las opciones son: - Prepared Statements: Los prepared statements son sentencias pre-compiladas, en las cuales se indica qu parmetros sern ingresados por el usuario. De esta forma podemos indicarle al DBMS cul es el cdigo a ejecutar y cules sern las variables. Esto permite que el motor distinga la sentencia a ejecutar de los datos de entrada y as evitar que el usuario agregue sentencias SQL. Adems de la obvia ventaja de prevenir las inyecciones, stos permiten mejorar el tiempo de ejecucin si la misma sentencia se utiliza ms de una vez. Cuando armamos una sentencia SQL, el motor de base de datos analiza, compila y optimiza la forma en que la ejecutar, por ello, si la armamos una sola vez y la ejecutamos varias veces (ya sea utilizando los mismos o diferentes parmetros), el tiempo de ejecucin disminuir considerablemente. - Stored Procedures: Los stored procedures son ms conocidos que los prepared statements entre los desarrolladores debido a su uso extensivo en la programacin que interactua con BDs. Se escriben procedimientos en el lenguaje del DBMS (los cuales se almacenan en la base de datos) y desde el cdigo del programa se llaman estos procedimientos con las variables ingresadas por el usuario como los parmetros. De esta forma, el DBMS puede distinguir correctamente variables de cdigo. Los stored procedures son tan eficaces como los prepared statements, pero hay que tener cuidado de no utilizar los parmetros para armar sentencias, porque estaramos anulando la defensa. - Escapar todo dato ingresado por el usuario: probablemente sta sea la forma ms difundida de prevenir SQL Injection y la cual se cita en mucha bibliografa, pero tambin es la menos recomendada. La idea es que cada vez que el usuario ingrese datos que utilizaremos en una sentencia, escapemos los caracteres especiales (como comillas simples o dobles, barras invertidas "\", o caracteres de comentario "--" o "#", etc) para que el dato sea un solo string y el motor de BD no lo confunda con cdigo a ejecutar. Un ejemplo de funcin que permite escapar los caracteres especiales, cuando utilizamos php + mysql, es mysql_real_escape_string. Recuerden que esta funcin no es mgica y se puede evitar como expliqu en el artculo SQL Injection avanzado: consultas simplificadas, file inclusin, ejecucin remota y ms! en la seccin "No puedo usar comillas... no importa!". Cmo explican en la pgina de OWASP citada anteriormente, este tcnica es frgil y se debe utilizar slo cuando ya contamos con cdigo inseguro y reescribirlo utilizando prepared statements requerira un costo inaceptable. Todo desarrollo que se comience de cero debe utilizar prepared statements o stored procedures.

Librera PDO para PHP

Como ya les he mencionado varias veces, soy fan del desarrollo en PHP, por lo cual me expandir un poco sobre cmo prevenir SQL Injection en este lenguaje. Mi opcin favorita para tal caso es utilizar prepared statements, porque mi filosofa es "dejar la base de datos, para almacenar datos", lo cual elimina a los stored procedures como opcin. Adems de esta forma el cdigo es portable para ser utilizado con distintos motores de base de datos.

En fin, para lograr el objetivo utilizaremos la librera PDO. Como lo describen en la documentacin oficial de PHP, PDO (PHP Data Objects) es una interfaz ligera para acceder a bases de datos, la cual permite abstraernos del motor de base de datos, utilizando siempre el mismo conjunto de funciones para acceder a los datos. Esto hace que el cdigo sea portable a cualquiera de las bases de datos soportadas por PDO, entre las que se encuentran las ms importantes (MySQL, SQL Server, Postgre, Oracle, SQLite, Informix, Firebird, etc).

PDO no fuerza al usuario a utilizar prepared statements, pero ofrece una serie de funciones para el manejo de los mismos. En ellas se puede utilizar tipado fuerte, lo cual agrega mayor seguridad al especificar el tipo de los datos esperados.

Utilizar los Prepared Statements de PDO

Los prepared statements de PDO son fciles de utilizar, aunque al principio pueden resultar un poco molestos debido a que se debe escribir un poco ms de cdigo al ejecutar consultas. Veamos una comparacin de las funciones que ms se utilizan al acceder a la base de datos. Tomo como ejemplo las funciones de MySQL, aunque como saben, PDO funciona con casi cualquier base de datos.

Conexin con la base de datos: $db = mysq_connect('localhost', 'tester', '123456'); mysql_select_db('test', $db);

Para conectarnos utilizando PDO debemos crear un objeto PDO indicando el tipo de la base de datos a utilizar, el host, el nombre de usuario y el password de la siguiente manera: $db = new PDO("mysql:host='localhost';dbname='test'", 'tester', '123456'); Consulta a la base de datos: mysql_query("SELECT * FROM content WHERE id=".$_GET['id'], $db)

Esta es la parte ms sensible donde debemos evitar utilizar cualquier parmetro ingresado por el usuario directamente en el string de consulta. Primero preparamos la consulta que debemos ejecutar, indicando con : cules son las variables ingresadas por el usuario: $stmt = $db->prepare("SELECT * FROM content WHERE id= :id"); Luego vinculamos la variable ingresada por el usuario con la utilizada en la consulta, indicamos el tipo, y finalmente ejecutamos la consulta. $stmt->bindParam(":id", $_GET['id'], PDO::PARAM_INT); $stmt->execute(); Las 2 llamadas anteriores se pueden unificar en una sola, utilizando un arreglo asociativo en la funcin execute: $stmt->execute(array(":id" => $_GET['id'])); La nica desventaja de utilizar esta forma de bind es que todos los parmetros son tratados como PDO::PARAM_STR (es decir, strings). Obtener una fila en un arreglo: mysql_fetch_array($db);

La forma de obtener filas es muy similar a la llamada anterior, lo que debemos ejecutar es lo siguiente: $stmt->fetch(); Al igual que mysql_fetch_array, el fetch de PDO obtiene por defecto un arreglo con ambos ndices: asociativo y numrico. Si deseamos obtener slo el arreglo asociativo, podemos utilizar el

parmetro PDO::FETCH_ASSOC. En caso de desear uno con ndices numricos el parmetro es PDO::FETCH_NUM: $stmt->fetch(PDO::FETCH_ASSOC) o $stmt->fetch(PDO::FETCH_NUM); Adems contamos con una funcin que retorna todas las filas resultantes de la ejecucin de la consulta: $stmt->fetchAll(); Ver errores: mysql_error($db);

En el caso de prepared statements, los errores se obtienen con errorInfo: $stmt->errorInfo(); el cual retorna un arreglo que contiene: 0 cdigo de error SQLSTATE 1 cdigo de error 2 mensaje de error

Bueno, con eso cubrimos las funciones bsicas de acceso a la base de datos, aunque existen unas cuantas ms y las pueden ver en el manual The PDOStatement class. Conclusin Prevenir las inyecciones SQL es muy sencillo utilizando prepared statements y es slo cuestin de ser lo suficientemente responsable (y consciente) a la hora de escribir consultas. Una vez que uno se acostumbra, la escritura del cdigo sale de forma natural y esas "lneas de ms" no resultan tan molestas. Recuerden que esto les salvar muchos dolores de cabeza y se sentirn tranquilos de que no los "hackearan" utilizando esta tcnica. Write safely! =) http://itfreekzone.blogspot.mx/2011/04/como-evitar-sql-injection-prepared.html