Explicación de las vulnerabilidades de inyección SQL

En este episodio, revisaremos algunos ejemplos de inyección SQL y discutiremos por qué es tan importante que siempre asumas que, en la web, una persona es culpable hasta que se demuestre su inocencia.

Que aprenderemos

  • SQL Injection
  • Prepared Statements
  • Parameter Binding

Pasar parámetros por el query string, por ejemplo http://localhost:8888/?id=1

Para acceder a los parámetros del query string usamos $_GET Podemos usarlo así:

$config = require('config.php');
$db = new Database($config['database']);
dd($_GET);

Entonces una de las formas que podemos desplegar el post id que nos pasen por el query string podría verse así:

$config = require('config.php');
$db = new Database($config['database']);

$id = $_GET['id'];

$posts = $db->query("select * from posts where id = {$id}")->fetch();
dd($posts);

Y si funciona, pero, el problema que tenemos es que introducimos una invulnerabilidad muy grabe, ya que estamos permitiendo al usuario entrar código SQL a nuestro código. Imagina que un usuario mal intencionado escribe un query string como el siguiente: localhost:8888/?id=1; drop table users; Como puedes ver, esto seria desastroso para nuestra aplicación.

Una solución podría ser, pasar el la variable $id como un parámetro al método $db->query() en index.php así:

$config = require('config.php');
$db = new Database($config['database']);

$id = $_GET['id'];
$query = "select * from posts where id = ?";

// bind (unir) $id
$posts = $db->query($query, [$id])->fetch();
dd($posts);

Y ahora preparamos que pueda recibir el parámetro el método en Database.php así:

public function query($query, $params = []){
	$statement = $this->connection->prepare($query);
	$statement->execute($params);
	return $statement;
}

Ya funciona, aunque en el query string metamos datos maliciosos así: http://localhost:8888/?id=1;%20drop%20table%20users; El sistema nos regresa correctamente el post 1.

Una mejora adicional es constringir el parámetro y darle el valor de una llave :id (key), en index.php así:

$config = require('config.php');
$db = new Database($config['database']);

$id = $_GET['id'];
$query = "select * from posts where id = :id";

// bind (unir) $id
$posts = $db->query($query, [':id' => $id])->fetch();
dd($posts);

Listo! Funciona, tambien podriamos si queremos omitir los : en:

$posts = $db->query($query, ['id' => $id])->fetch();
dd($posts);

Listo! Funciona igual.

Nota! Lo mas importante que aprendimos en este tema es que Nunca de los Nunca colocar código en linea in-line de parámetros SQL a nuestro $query.