PDO en PHP: ¿Salva al 100% de las inyecciones SQL?

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: Mitos y realidad de PDO

\n\n

Si programas en PHP y alguna vez has buscado en Google "cómo protegerse de las inyecciones SQL", seguramente has escuchado el mantra: "Usa PDO y sentencias preparadas, y serás feliz". Es el estándar de oro, la base de cualquier manual de seguridad en PHP. Pero, ¿es realmente así? ¿Basta con escribir $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?') y olvidarse del problema?

\n\n

La pregunta "¿Son suficientes las sentencias preparadas de PDO para prevenir la inyección SQL?" acumuló miles de visitas en Stack Overflow por una razón. La respuesta, resulta, no es tan sencilla. En este artículo, nos meteremos bajo el capó de PDO, analizaremos cómo funcionan realmente las consultas preparadas y descubriremos en qué casos incluso PDO puede fallarte.

\n\n

Esto no es teoría aburrida. Veremos ejemplos reales, emulación de consultas, detalles sobre codificaciones y daremos acciones concretas para que tu código esté verdaderamente protegido.

\n\n

¿Cómo funcionan las Sentencias Preparadas: Magia o matemáticas?

\n\n

Para empezar, recordemos qué es una inyección SQL. Ocurre cuando un atacante envía datos en una consulta que se interpretan como parte del código SQL. El clásico: ' OR 1=1 --.

\n\n

Las sentencias preparadas resuelven este problema de raíz. Separan la lógica de la consulta de los datos. Se ve así:

\n\n
<?php\n$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');\n$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');\n$stmt->execute([':email' => $_GET['email'], ':status' => 'active']);\n?>\n
\n\n

Bajo el capó ocurre lo siguiente:

\n\n
    \n
  1. Preparación (Prepare): El controlador envía al servidor de la BD el "esqueleto" de la consulta: SELECT * FROM users WHERE email = ? AND status = ?. El servidor analiza este esqueleto, lo optimiza y guarda el plan de ejecución. El lugar para los datos (marcadores de posición) está vacío por ahora.
  2. \n
  3. Ejecución (Execute): Ahora envías solo los valores reales. El servidor los inserta en el plan ya preparado, pero lo hace de una manera especial: sabe que son datos, no código. Por lo tanto, cualquier intento de "cerrar la comilla" o agregar un comando SQL simplemente se convierte en parte de un literal de cadena.
  4. \n
\n\n

Suena ideal. Pero hay un detalle.

\n\n

El escollo: Emulación de Sentencias Preparadas

\n\n

La principal sorpresa que reveló esa pregunta en Stack Overflow es: PDO por defecto no siempre usa sentencias preparadas "reales".

\n\n

El problema es que no todas las bases de datos admiten esta función a nivel de protocolo. Y algunos controladores (por ejemplo, para SQLite o versiones antiguas de MySQL) funcionan mediante emulación. ¿Qué significa esto?

\n\n

Cuando la emulación está activada, PDO sustituye los valores en la consulta SQL del lado del cliente (en PHP) y luego envía la consulta ya completa al servidor. Al hacerlo, escapa los caracteres especiales usando PDO::quote().

\n\n

Y aquí empiezan los problemas. La emulación es, en esencia, la misma sustitución de cadenas, pero realizada por la biblioteca. Y si configuras incorrectamente la codificación de la conexión, este mecanismo puede ser vulnerado.

\n\n

Ejemplo: Ataque mediante diferentes codificaciones

\n\n

El ataque clásico usando una codificación incorrecta se ve así. Supongamos que estableces una conexión con MySQL en codificación GBK (codificación china), y la consulta se emula. El carácter \\ (barra invertida) en GBK puede ser parte de un carácter multibyte.

\n\n

El atacante envía la cadena: %BF%27. En codificación GBK, la secuencia %BF%5C forma un solo carácter, y %27 es simplemente una comilla. El emulador ve la comilla y la escapa, convirtiendo %27 en %5C%27 (es decir, \\'). Pero debido a que

Blogs

Book Recommendations