Notice: Article only available in Spanish!
Hemos visto en publicaciones anteriores cómo conectar a una base de datos mysql desde php, y cómo usar la función mysql_query para consultar datos.
Ahora vamos a hablar de seguridad en php. La aparente sencillez de uso de php muchas veces oculta su potencia, y no tener en cuenta ciertos detalles puede hacer que nuestra aplicación se vaya al traste con un solo ‘click’. Recordemos nuestra anterior función readTable
/**
* retrieves the selected fields from a certain table in a database
* @param table to retrieve from
* @param fields to retrieve
*/
function readTable($table,$fields,$identifier)
{
//variables
$resultsArray = array();
//fields
$fieldsList = "";
foreach($fields as $key=>$value)
{
$fieldsList .= $value . ",";
}
//remove last comma
$fieldsList = substr($fieldsList,0,strlen($fieldsList)-1);
//query
$query = "SELECT " . $fieldsList . " FROM " . $table;
$resultset = @mysql_query($query,$identifier);
if (@mysql_num_rows($resultset) > 0)
{
$i = 0;
while ($result = mysql_fetch_array($resultset))
{
$resultsArray[$i] = $result ;
$i++;
}
}
return($resultsArray);
}
Resumiendo, la llamada a la función readTable(‘superheroes’,$fields,$token); podría permitir ejecutar código sql que no deseamos, y que podría dejar al aire nuestra aplicación.
Por ejemplo, la llamada
$results = readTable('superheroes, users','*',$token);
Suponiendo que tenemos una tabla llamada users con información personal, claves de acceso, etc, la llamada a esta función ejecutaría la sentencia SQL
$query = "SELECT * FROM superheroes, users";
la cual nos devolvería absolutamente todos los campos de la tabla users.
Si nuestro script permite la llamada de esta función con parámetros que se pasan desde la url del navegador, por ejemplo
$results = readTable($_GET["table"],$_GET["fields"],$token);
sería realmente fácil para cualquier hacker no muy experimentado, atacar nuestro sistema con “sql injection”.
Otro ejemplo habitual sucede en las autenticaciones de usuarios. Supongamos que controlamos el acceso de un usuario a una intranet con la siguiente sentencia sql
SELECT * FROM `users` WHERE `user` = '$_POST[usuario]' && `pass` = '$_POST[pass]'
la cual recibe valores de un formulario html. Bien, si los datos introducidos por el usuario son
Login: user
Password: '' || 1 = '1'
entonces la consulta quedará así
SELECT * FROM `users` WHERE `user` = 'user' && `pass` = '' || 1 = '1'
la cual nos autenticará siempre (|| 1 = ‘1’, es decir, o 1 = 1 siempre dará 1 booleano).
(Ejemplo propuesto por Issel Guberna)
Solución
La solución pasa por filtrar las variables en nuestro script principal, impidiendo que tome valores cualquiera de las variables $_GET[] y $_POST[] que le están proporcionando datos. Por ejemplo:
$table = $_GET["table"];
list ($table) = split(" ", $table, 1); //we get only the first value
if ($table == "users")
die('cannot access directly');
Esto es un mero ejemplo, ya que en algunos casos nos interesará consultar los datos de los usuarios y querremos permitir la lectura de ciertos campos. La clave está en hacer un uso inteligente de la carga de parámetros por $_GET, leer únicamente los que sean estrictamente necesarios y filtrarlos para que sólo puedan tomar los valores que queremos permitir, bien con un switch, eliminando ciertas palabras clave de sintaxis sql de la cadena de entrada, etc.