Inyección SQL en PHP 2025: ¿cómo protegerse? Guía completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué la inyección SQL sigue siendo relevante en 2025?



Imagine: ha creado un sitio web genial en PHP, agregó un formulario de contacto, búsqueda de productos, y una semana después descubre que un atacante ha descargado toda la base de datos de clientes. ¿Suena a historia de terror de los 2010? Lamentablemente, según OWASP, la inyección SQL (SQL injection) sigue estando entre las 3 vulnerabilidades más peligrosas de las aplicaciones web. Y sí, en 2025 los desarrolladores todavía cometen este error.



La pregunta "How can I prevent SQL injection in PHP?" en Stack Overflow ha acumulado miles de visitas y cientos de respuestas. Pero el problema no es la falta de información; el problema es que muchos todavía usan métodos obsoletos como las funciones mysql_* (que se eliminaron en PHP 7.0) o confían ingenuamente en mysqli_real_escape_string() como una bala de plata.



En este artículo, analizaremos las mejores prácticas de protección contra inyecciones SQL en PHP en 2025. Aprenderá por qué las consultas preparadas (prepared statements) son el estándar de la industria, cómo trabajar correctamente con PDO y mysqli, y qué trampas aún esperan a los desarrolladores sin experiencia. ¡Allá vamos!



¿Qué es la inyección SQL en palabras sencillas?



La inyección SQL es un ataque en el que un atacante "inyecta" código SQL malicioso en una consulta a través de la entrada del usuario (formularios, URL, cookies). En lugar de simplemente pasar un nombre de usuario, el hacker puede enviar: ' OR '1'='1 — y obtener acceso a todos los datos.



Aquí hay un ejemplo clásico de código vulnerable:



// ¡NUNCA haga esto!$username = $_POST['username'];$password = $_POST['password'];$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";$result = mysqli_query($conn, $query);


Si el atacante ingresa en el campo username: admin' --, la consulta se convierte en:



SELECT * FROM users WHERE username = 'admin' --' AND password = '...'


Todo lo que está después de -- es un comentario. La autenticación está superada. La base de datos es nuestra. ¡Hola, fuga de datos!



Método n.º 1 (y el más importante): Consultas preparadas (Prepared Statements)



En 2025, esta es la única forma recomendada de protegerse contra las inyecciones SQL. Las consultas preparadas separan la lógica SQL de los datos. Primero le dice a la base de datos: "Aquí está la plantilla de la consulta, aquí están los lugares para las variables", y luego pasa los datos por separado. La base de datos misma escapa todo lo que sea necesario.



Opción A: PDO (PHP Data Objects) — el estándar moderno



PDO admite 12 sistemas de gestión de bases de datos diferentes (MySQL, PostgreSQL, SQLite y otros). Si está escribiendo un nuevo proyecto en 2025, use PDO.



<?php// Conexión a la BD a través de PDO$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';$user = 'root';$password = '';

try { $pdo = new PDO($dsn, $user, $password); // Importante: lanzar excepciones en caso de error $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);} catch (PDOException $e) { die('Error de conexión: ' . $e->getMessage());}

// Consulta preparada con marcadores de posición nombrados$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');$stmt->execute([ ':email' => $_POST['email'], ':status' => 'active']);$user = $stmt->fetch(PDO::FETCH_ASSOC);

// Alternativa con marcadores de posición posicionales (?)$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status = ?');$stmt->execute([$_POST['email'], 'active']);$user = $stmt->fetch();?>


¿Por qué es seguro? Porque los datos nunca se insertan directamente en la cadena SQL. Incluso si el usuario pasa ' OR '1'='1, PDO lo pasará como un literal de cadena, y la base de datos lo interpretará exactamente como una cadena, no como parte del comando SQL.



Opción B: MySQLi (MySQL Imp

Blogs

Book Recommendations