domingo, 10 de marzo de 2013

PHP - Evitar inyección SQL

Uno de los mayores riesgos que pueden aparecer a la hora de crear una página web que haga uso de Bases de Datos SQL es la inyección SQL. Esto se trata, en resumidas cuentas, de la posibilidad de poder forzar a la base de datos a devolver una respuesta que nos convenga para poder hacer un uso inapropiado del portal web.

Un ejemplo sería un formulario de inicio de sesión. En una página web en la que no controlamos este problema ¿qué ocurriría si en el campo usuario ponemos un nombre de usuario correcto y en la contraseña escribimos "' OR '1'='1'"? La respuesta es sencilla: es muy probable que tengamos acceso a la cuenta de ese usuario sin conocer su contraseña.

Ahora que ha quedado claro la gravedad del asunto, voy a poneros unos cuantos puntos a tener en cuenta a la hora de crear formularios que ataquen a nuestra base de datos que harán que, en casi la totalidad de los casos, sea imposible provocar la inyección SQL. 

Hay que mencionar que estos trucos son válidos para páginas web que hagan uso de PHP para atacar a Bases de Datos SQL. De no ser así, es posible que algunos de ellos no puedan ponerse en práctica.

  • Se debe utilizar siempre comillas simple a la hora de delimitar las variables que se vayan a utilizar directamente sobre la consulta a la Base de Datos. Esto hace que los intentos de inyección SQL que no hagan uso de comillas simples no tengan efecto sobre nuestro sistema. Ejemplo:
SELECT usuario FROM tabla_usuarios WHERE usuario= ' $usuario ';
  • Si en alguno de los campos del formulario esperas un tipo de datos concreto (un número, string, etc...), es recomendable hacer uso de la función de PHP gettype() antes de utilizar el dato que nos indica el usuario, ya que devuelve una ristra indicando su tipo.
  • Existe la posibilidad de comprobar si nuestro servidor cambia automáticamente las ristras pasadas por formulario o por URL para evitar la inyección SQL. Esto se puede averiguar ejecutando el siguiente código:
<?if(!isset($_GET["inyeccion"])){ header("location: ?inyeccion='");} else { echo 'Tu servidor '; if($_GET["inyeccion"] != "'")echo 'no '; echo 'tiene problemas de inyección';}?>

  • En caso de que se haya mostrado el mensaje "Tu servidor tiene problemas de inyección", se debería filtrar cada una de las variables obtenidas por formulario o URL haciendo uso del siguiente código, encargado de, por ejemplo, cambiar los caracteres ' por \':
<?// Se filtran las variables pasadas por URL
foreach($_GET as $var=>$valor){   $_GET[$var] = str_replace("'","\'",$_GET[$var]);}// Se filtran las variables pasadas por formularioforeach($_POST as $var=>$valor){   $_POST[$var] = str_replace("'","\'",$_POST[$var]);}?>

  • Se recomienda no hacer uso del operador * en queries SQL, sino indicar explícitamente aquellos campos que se quieren recuperar en la llamada. De este modo, además de evitar que los usuarios del portal puedan acceder a datos que no deberían conocer, hará que la llamada a la Base de Datos sea más óptima y rápida.
  • Hacer uso del usuario root es muy peligroso y debe hacerse con mucha precaución. Si nuestros usuarios de la página sólo van a tener acceso a la visualización de determinadas tablas de nuestra Base de Datos, es recomendable crearles un usuario en nuestro esquema con los permisos correspondientes.
  • Hay que tener mucho cuidado con controlar los mensajes de error que se muestran en el portal. Por defecto, si ocurre algún error al realizar la llamada a la Base de Datos, se muestran mensajes en la parte superior que pueden contener más información de la que los usuarios deberían obtener, además de evidenciar que algo no va bien en nuestro portal. Es por eso que, cuando se vaya a abrir el portal al público, incluyamos siempre una llamada a la función de PHP error_reporting(0), que evita que aparezcan estos mensajes.
  • Por si todo esto no fuera suficiente, existe otra funcion PHP que evitará la inyección SQL. Se trata de mysql_real_scape_string, una función que se está quedando obsoleta (tengo que informarme sobre las nuevas incluidas en las últimas versiones de PHP), a la cual se le pasa una variable y la formatea para evitar problemas por inyección.
Como se ve, evitar la inyección SQL es bastante sencillo si se sigue una serie de pasos que acabarán convirtiéndose en una rutina cuando programemos.

Espero que os sea útil y recordad, toda duda o comentario constructivo es bien recibido y lo contestaré tan pronto como me sea posible.

1 comentario:

  1. Hola Muy bueno lo que explicas pero intento protegerme y aun sigue el problema

    ResponderEliminar