Una inyección SQL ocurre cuando un atacante consigue insertar código SQL arbitrario en una consulta destinada a la base de datos, aprovechando un campo de formulario, un parámetro de URL, o cualquier otro punto de entrada cuyo contenido se integra directamente en la consulta sin precaución. Este fallo, conocido desde hace décadas, sigue siendo uno de los más explotados en la web, especialmente en desarrollos PHP a medida donde la consulta SQL se construye manualmente en lugar de a través de un framework que impone una protección por defecto.

Lo que hace vulnerable a una consulta

El problema aparece en cuanto un valor proporcionado por el usuario se concatena directamente en una cadena de caracteres que forma la consulta SQL. Si un campo de búsqueda acepta cualquier texto y ese texto se inserta tal cual en la consulta, un visitante malintencionado puede introducir un valor que modifique la estructura lógica de la propia consulta, en lugar de simplemente aportar un dato a buscar. Esta manipulación puede permitir eludir una autenticación, extraer datos a los que el usuario no debería tener acceso, o en los casos más graves, modificar o eliminar datos de la base.

Por qué el escapado manual no basta

Una reacción habitual ante este riesgo consiste en intentar limpiar o escapar manualmente los caracteres especiales antes de insertarlos en la consulta, por ejemplo escapando los apóstrofos. Este enfoque sigue siendo frágil: depende de la memoria y el rigor del desarrollador en cada punto de entrada del sitio, y basta un solo olvido para reabrir el fallo. También puede resultar incompleto según el contexto de uso del dato dentro de la consulta, lo que lo convierte en una protección incierta en lugar de una solución robusta.

Las sentencias preparadas, una separación estricta entre código y dato

Las sentencias preparadas, disponibles de forma nativa en PHP mediante las extensiones PDO o MySQLi, resuelven este problema de raíz separando por completo la estructura de la consulta SQL de los valores que se insertan en ella. La consulta se envía primero a la base de datos con una forma fija, con espacios reservados para los valores variables, y luego esos valores se transmiten por separado y en ningún caso pueden interpretarse como código SQL, sea cual sea su contenido. Un atacante puede introducir cualquier cadena de caracteres, incluidas instrucciones SQL completas, pero esos caracteres seguirán tratándose como un simple dato textual sin efecto sobre la estructura de la consulta.

Un ejemplo concreto de la diferencia

Una consulta construida por concatenación directa suele parecerse a una cadena ensamblada con la variable del usuario insertada en medio del texto SQL. Una sentencia preparada con PDO separa esta lógica: la consulta contiene un espacio reservado, normalmente representado por un signo de interrogación o un nombre precedido de dos puntos, y el valor real se proporciona por separado en el momento de la ejecución, mediante un método dedicado del objeto de conexión. Esta diferencia estructural, una vez comprendida, no requiere más líneas de código que una concatenación manual, contrariamente a una idea a veces extendida.

Lo que las sentencias preparadas no cubren

Las sentencias preparadas protegen específicamente contra la inyección SQL, pero no eximen de las demás buenas prácticas de seguridad de aplicaciones, como validar el formato esperado de un dato antes incluso de enviarlo a la base, o escapar adecuadamente los datos mostrados en una página HTML para prevenir otro tipo de fallo llamado inyección de script. Un formulario que acepta un correo electrónico debe, por ejemplo, verificar que el dato se parece a un correo antes de procesarlo, independientemente de la protección que aporte la propia sentencia preparada.

El caso de los ORM y los frameworks modernos

Los frameworks PHP habituales y las bibliotecas ORM usan sentencias preparadas internamente por defecto, lo que protege automáticamente al desarrollador siempre que use los métodos proporcionados por esas herramientas en lugar de construir manualmente una consulta bruta dentro de esos mismos frameworks. En un desarrollo totalmente a medida sin framework, la responsabilidad de sistematizar las sentencias preparadas recae por completo en los hábitos de codificación adoptados desde el inicio del proyecto, lo que hace aún más importante convertirlo en una regla innegociable en lugar de una opción puntual.

Puntos a verificar antes de publicar: confirmar que la descripción del funcionamiento de PDO y MySQLi sigue siendo exacta para las versiones de PHP actualmente en soporte activo, y comprobar que la distinción entre inyección SQL e inyección de script corresponde efectivamente a mecanismos de protección distintos, sin confundirlos en la redacción.