Oct
13

Protegiendo aplicaciones contra los ataques de inyección SQL

Las inyecciones SQL, son un tema que todo programador debe tener en cuenta, debido a que TODO sistema realizado para acceso Web que interactúe con bases de datos y datos originarios de formularios requiere su atención. No es un tema dificil de entender ya que está ampliamente documentado en la Web. Pero, no obstante, para muchos es un dolor de cabeza, por lo que trataré de reducirles todo a un sencillo y único paso: Filtrar todo dato originario del exterior.

Un ejemplo de como confiar en datos extraños podría desbaratar nuestra lógica, permitiendo comportamientos inesperados, es el siguiente:


// Consultar la base de datos para verificar si hay una coincidencia de usuario
$consulta = "SELECT * FROM usuarios WHERE usuario='{$_POST['username']}' AND password='{$_POST['password']}'";
mysql_query($consulta);

// No revisamos $_POST['password'], ¡podría ser cualquier cosa que el usuario
// quiera! Por ejemplo:
$_POST['username'] = 'admin';
$_POST['password'] = "' OR ''='";

// Esto quiere decir que la consulta enviada a MySQL sería:
echo $consulta;

Por lo que como resultado devolvería

SELECT * FROM usuarios WHERE usuario=’admin’ AND password=” OR ”=”

Permitiendo el inicio de sesión sin una contraseña valida, por lo que recurriremos al tema de…

Filtrado de datos

El proceso de validar los datos e ir dejando fuera datos inválidos, es indiscutiblemente el principio básico de la seguridad en las aplicaciones Web. Una de las prioridades básicas al desarrollar una aplicación Web, es absolutamente simple: Nunca confíes en “ningún” dato extraído del exterior, especialmente información originaria de formularios. (Nunca jamás!)

Registros Globales

En PHP 4.2.0, el valor por default para la directiva register_globals fue cambiado a “off”, sin embargo, aún existen aplicaciones que actualmente funcionan con register_globals (Algunas que tengo por ahí perdidas en mi disco duro :P jeje). Actualmente, creo que los desarrolladores de PHP tienen previsto que para la versión 6 de PHP, register_globals haya dejado de ser utilizada completamente por lo que también creo que será eliminada, (No me hagan mucho caso, no me consta).

Cuando activamos la directiva en “On”, register_globals importa datos de diferentes fuentes en el ámbito global de nuestro script. Por ejemplo $_SESSION, $_COOKIE, $_GET, $_POST las define como variables directamente accesibles. Por ejemplo: http://ejemplo/script.php?val1=uno&val2=dos&val3=tres Dentro de script.php, lo correcto sería que mandáramos llamar a los valores de la siguiente forma:

$val1 = $_GET['val1']
$val2 = $_GET['val2']
$val3 = $_GET['val3']
Con register_globals activado, esto ya no es necesario. Podemos tener acceso a las variables directamente de la forma…
 echo “$val1 $val2 $val3”; 
…y funcionaría correctamente. De igual manera funcionaría si los datos estuvieran en $_POST, $_COOKIE, o $_SESSION (No tengo idea de que pasaría si existieran claves iguales entre $_POST, $_COOKIE, $_GET y $_SESSION. Aunque, hmm… yo me imagino que habría un completo desorden y valores perdidos :-S ).

Aunque este comportamiento es simple y bien documentado, register_globals implica serios problemas durante el filtrado de datos. (Así que ya saben ¡Prohibido usarlo! :P )

Ahora, unas cuantas recomendaciones (prácticamente obligadas) para trabajar en servidores con register_globals activados (Y aunque no lo crean, conozco dos servidores públicos que los siguen utilizando, a pesar de las malas críticas:-S ):

  • Inicializa siempre todas las variables.
  • Desarrolla tus aplicaciones con error_reporting definido en E_ALL.
  • Filtra todos los datos que provengan del exterior.

Y ahora si, entrando ya en materia…

Inyecciones SQL (SQL Injection)

Cuando trabajamos con bases de datos, se espera normalmente que ésta interactúe con datos originarios de formularios. Por ejemplo para almacenar datos de un usuario, o simplemente obtener datos a partir de una identidad única.

No obstante la razón, por utilizar datos originarios de formularios posee un muy enorme y comprometedor riesgo. La mayoría de este tipo de vulnerabilidades se debe al resultado del mal filtrado de datos.

Y ahora entrando más profundo en materia… ¿Cómo filtrar los !”#$tos datos?

Si tenemos una consulta de la forma

$sql = “INSERT INTO tabla VALUES ($valor)”;

El riesgo puede disminuirse encerrando con comillas simples el valor.

$sql = “INSERT INTO tabla VALUES (`$valor`)”

Siempre y cuando $valor no contenga comillas simples sin escapar, la consulta SQL puede realizarse con éxito. Sin embargo, esto no es suficiente para asegurar la seguridad de nuestro script, por lo que tenemos que ir más allá del simplemente poner comillas simples. PHP nos ofrece funciones específicas para esta tarea, por ejemplo: En el caso de MySQL pueden confiar ciegamente en mysql_real_escape_string() (Y en otros casos… ya que me entere como se hace, les digo :P).

Links

6 Comments

Make A Comment

Comments RSS Feed   TrackBack URL

Leave a comment

top