Consultas preparadas SQL

Hoy vamos a ver un ejemplo de consultas preparadas para evitar las inyecciones SQL.

Una consulta preparada es una consulta que permite mejorar la seguridad en una base de datos. Para esto utilizaremos los parámetros con el signo interrogante ‘?’.

consultas preparadas

Cómo funcionan las consultas preparadas?

En primer lugar, prepararemos una plantilla de la sentencia SQL, dejando algunos valores representados por un interrogante ‘?’.

En segundo lugar, la base de datos realiza la optimización de la consulta SQL, y guarda el resultado.

Por último, la aplicación enlaza valores con los parámetros y la base de datos ejecutará la sentencia.

Ventajas de las consultas preparadas

Las consultas reparadas reducen en tiempo de análisis, porque la preparación de la consulta de realiza sólo una vez.

Los parámetros enlazados minimizan el ancho de banda del servidor, ya que sólo necesitas enviar los parámetros cada vez y no la consulta entera.

Las consultas reparadas son muy útiles frente a inyecciones SQL, ya que los valores de los parámetros no necesitan ser “escapados”.

Ahora vamos a nuestro código.

Index.html

<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <title>Inyección SQL</title>
</head>

<body>
    <h1>Inyección SQL con Sqlmap</h1>
        <div><a href="entrada.php?id=1">Ir a...</a></div>
        <div><a href="entrada_preparada.php?id=1">Ir a...</a></div>
</body>

</html>

En la línea 12, cuando pasamos la variable id por el GET de la url, podemos tener un problema de vulnerabilidad. Aquí, en principio no llevamos a cabo ninguna acción.

Entrada_preparada.php

En este archivo, preparamos la consulta preparada.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Inyección SQL con Sqlmap</title>
</head>

<body>
    <h1>Ejemplo básico con GET</h1>
    <?php 
        if(isset($_GET["id"])){ 
            $con = mysqli_connect("localhost","root","","inyeccion");
            
            // Prepara la consulta con el parámetro id
            $select = "SELECT nombre, email FROM usuarios WHERE id=?;";
            $sqlPreparada = mysqli_prepare($con, $select);
            
            // Enlaza el parámetro id
            mysqli_stmt_bind_param($sqlPreparada, 'i', $id);
            
            // Ejecuta la consulta
            $id=$_GET["id"];
            mysqli_stmt_execute($sqlPreparada);
            
            // Enlaza las columnas
            mysqli_stmt_bind_result($sqlPreparada, $nombre, $email);
            
            // Muestra la consulta
            mysqli_stmt_fetch($sqlPreparada);
            echo "<h2>Bienvenid@</h2>";
            echo "Hola: ".$nombre."<br>";
            echo "Email: ".$email."<br>";
        }
        else{
            header("location: index.html");
        }
    
        mysqli_close($con);
    ?>
    
</body>
</html>

En definitiva, las consultas preparadas son necesarias para evitar las inyecciones SQL. Sin embargo, no podemos asegurarnos al cien por cien.

En el código del archivo entrada_preparada.php en las líneas 16, 19, 23, 26 y 29 nos encontramos las funciones PHP para preparar la consulta SQL de la línea 15.

Resumiendo, podemos hablar de 5 pasos:

  1. Preparar la consulta con la función mysqli_prepare().
  2. Enlazar el parámetro, con la función mysqli_stmt_bind_param().
  3. Ejecutar la consulta, con la función mysqli_stmt_execute().
  4. Enlazar las columnas de la consulta, con la función mysqli_stmt_bind_result().
  5. Mostrar el resultado de la consulta, con la función mysqli_stmt_fetch().

En conclusión con este conjunto de funciones PHP podemos realizar consultas preparadas contra las inyecciones SQL.

Si alguien necesita la base de datos, aquí tenéis el código MySQL de la base de datos inyeccion.

Inyeccion.sql

-- phpMyAdmin SQL Dump
-- version 4.7.7
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 25-02-2018 a las 18:24:13
-- Versión del servidor: 10.1.28-MariaDB
-- Versión de PHP: 7.1.11

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Base de datos: `inyeccion`
--
CREATE DATABASE IF NOT EXISTS `inyeccion` DEFAULT CHARACTER SET utf8 COLLATE utf8_spanish_ci;
USE `inyeccion`;

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `usuarios`
--

CREATE TABLE `usuarios` (
  `id` int(11) NOT NULL,
  `nombre` varchar(20) COLLATE utf8_spanish_ci NOT NULL,
  `email` varchar(20) COLLATE utf8_spanish_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci;

--
-- Volcado de datos para la tabla `usuarios`
--

INSERT INTO `usuarios` (`id`, `nombre`, `email`) VALUES
(1, 'juan', 'juan@email.com'),
(2, 'mario', 'mario@email.com');

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `usuarios`
--
ALTER TABLE `usuarios`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `usuarios`
--
ALTER TABLE `usuarios`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Por último, os dejo un pantallazo de la herramienta SQLmap, que muestra la no vulnerabilidad de nuestro archivo entrada_preparada.php.

consultas preparadas sqlmap

También puede interesarte

Inyección SQL, usando GET

Insertar registros en MySQL

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *