Como muchos sabrán uno de los ataques más sencillos y peligrosos que cualquiera puede hacer es un ataque de SQL-Injection, estos ataques consisten en inyectar un query maligno a través de una entrada de usuario. Para explicarlo mejor veamos un ejemplo.

Digamos que al momento de autenticarnos en una aplicación es necesario que esta busque nuestro nombre de usuario y nuestra contraseña en la base de datos con el siguiente query:

SELECT * FROM User WHERE username = 'Juan Ejemplo' AND password = 'ejemplo';

Bien el código del servidor tomaría el nombre de usuario y el password y los agregaria en el comando que después se ejecutaría por el motor de bases de datos de preferencia.

Bien, qué pasaría entonces si un usuario malintencionado escribe esto como sus credenciales:

usuario:  Juan Ejemplo
password: '; DELETE FROM User WHERE ''='

Entonces el query resultante para estas credenciales seria:

SELECT * FROM User WHERE username = 'Juan Ejemplo' AND password = ''; DELETE FROM User WHERE ''='';

ven como un query se convierte ahora en 2 y ambos se ejecutan para borrar toda la tabla de usuarios?

Entonces es por eso que nunca, debemos concatenar un query y pasarlo directamente para ejecutarlo, no olviden la regla de oro:

Siempre utilizar declaraciones preparadas y queries parametrizados.

La mayoría de frameworks php no presentan este tipo de problemas ya que utilizan estos encantos, pero si no estan usando uno es necesario valerse de algunas herramientas.

Php Data Objects (PDO)

Utilizar objetos de datos de php para realizar los queries es facil y seguro, veamos un ejemplo:

//nos conectamos a la base de datos con un conneciton string
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'usuario', 'password');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

//obtenemos las credenciales del usuario
$usuario = $_POST['usuario'];
$password = $_POST['password'];

//preparamos nuestra declaracion
$stmt = $pdo->prepare("SELECT * FROM User WHERE username=? AND password=?");
//ejecutamos el query haciendo que pdo reemplace las variables.
$stmt->execute(array($usuario, $password));
//obtenemos los resultados
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

MySQLi

//conectarse
$mysqli = new mysqli('127.0.0.1', 'usuario', 'password', 'db');

//obtener
$username = $_POST['username'];
$password = $_POST['password'];

//preparar 
$stmt = $mysqli->prepare('SELECT * FROM User WHERE username = ? AND password = ?');
$stmt->bind_param('s', $username);
$stmt->bind_param('s', $password);

//ejecutar y obtener
$stmt->execute();
$result = $stmt->get_result();

Lo que pasa tras bambalinas en ambos casos es que preparamos las declaraciones de query que luego son parseadas y compiladas por el servidor de bases de datos, los signos de interrogación le dicen donde queremos filtrar (escapar) la declaración y luego nos devuelve un comando listo para ejecutarse. Lo importante aqui es que los valores de los parametros estan combinados con la declaración compilada y no son un SQL string, por lo que es seguro decir que una inyección sql es improbable cuando usamos estas medidas.

Conclusion

Proteger nuestras aplicaciones de ataques maliciosos puede ser una tarea sencilla cuando se tiene el conocimiento adecuado. Nuestros tests siempre deben incluir dichos ataques para comprobar que nuestra aplicación es segura y garantizar la integridad de nuestros datos.

Es recomendable también realizar copias de respaldo de nuestra base de datos con regularidad para evitar cualquier pérdida en caso de ataques malintencionados, y asegurarse que dichas copias de respaldo son recuperables en pocos minutos para asegurar la continuidad de la aplicación.

Categorized in: