Situación actual: PHP2Shell. Dos vulnerabilidades críticas de PHP (CVSS 9.2) permiten ejecutar código malicioso sin autenticación. Parches desde el 21/10/2026.Qué hacer ahora →

PHP y bases de datos: PDO y mysqli

PHP accede a las bases de datos mediante PDO o mysqli. Aquí ve las diferencias, las consultas seguras con sentencias preparadas y qué hacer con los accesos a la base de datos tras un incidente en el servidor.

Actualizado 6 min de lecturaConocimiento de PHP

En resumen: PHP accede a las bases de datos mediante dos interfaces extendidas: PDO (PHP Data Objects) trabaja con muchos sistemas de bases de datos mediante una interfaz uniforme; mysqli está pensada para MySQL y MariaDB. Con ambas, las sentencias preparadas, es decir, las consultas con marcadores de posición, protegen de forma fiable frente a la inyección SQL. Desde PHP 8.0 (PDO) y 8.1 (mysqli), ambas notifican los errores por defecto como excepción.

Cómo habla PHP con las bases de datos

Casi toda aplicación PHP guarda sus contenidos en una base de datos: páginas, pedidos, cuentas de usuario, ajustes. Para ello, el script PHP establece una conexión, envía una consulta en el lenguaje SQL y procesa el resultado. Las credenciales suelen estar en un archivo de configuración del mismo servidor.

Para la dirección esto es importante porque la base de datos suele contener los datos más valiosos de la empresa. Quien puede ejecutar código en el servidor web puede, por lo general, leer también el archivo de configuración y acceder a la base de datos con esas credenciales.

PDO y mysqli comparados

Característica PDO mysqli
Bases de datos muchas, mediante controladores como PDO_MYSQL, PDO_PGSQL, PDO_SQLITE, PDO_SQLSRV, PDO_OCI MySQL y MariaDB
Marcadores de posición con nombre (:name) y posicionales (?) posicionales (?)
Estilo orientado a objetos orientado a objetos y procedimental
Errores como excepción por defecto desde PHP 8.0.0 por defecto desde PHP 8.1.0
Forma abreviada para consultas con parámetros prepare() y execute() con array execute_query() desde PHP 8.2.0

En la práctica, suele decidir el framework o el CMS. Muchos incorporan una capa de base de datos propia que internamente utiliza PDO o mysqli. Quien se hace cargo de una aplicación puede averiguar con una sencilla búsqueda en el código fuente qué interfaz se utiliza y dónde se generan las consultas:

# ¿Dónde establece la aplicación conexiones y dónde se generan consultas?
grep -rn --include='*.php' -E 'new PDO|new mysqli|mysqli_connect|->query\(' /var/www/app/src | head -50

Los hallazgos con ->query( no son automáticamente inseguros. Pero merecen una segunda mirada, porque allí los valores pueden acabar directamente en el texto SQL en lugar de pasarse mediante marcadores de posición. Precisamente los proyectos antiguos mezclan ambos estilos y la revisión compensa antes de cada actualización importante.

Sentencias preparadas: el patrón más importante

En una sentencia preparada, la consulta se envía a la base de datos con marcadores de posición y los valores se pasan por separado. Según el manual de PHP, esto protege frente a la inyección SQL si todas las entradas del usuario pasan por marcadores de posición.

<?php
declare(strict_types=1);

$pdo = new PDO(
    'mysql:host=localhost;dbname=shop;charset=utf8mb4',
    $dbUser,
    $dbPass,
    [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);

$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE plz = :plz AND aktiv = :aktiv');
$stmt->execute(['plz' => $plz, 'aktiv' => 1]);

foreach ($stmt as $zeile) {
    echo htmlspecialchars($zeile['name'], ENT_QUOTES, 'UTF-8'), "\n";
}

Lo mismo con mysqli en la forma abreviada que existe desde PHP 8.2:

<?php
$db = new mysqli('localhost', $dbUser, $dbPass, 'shop');
$ergebnis = $db->execute_query('SELECT id, name FROM kunden WHERE plz = ?', [$plz]);

foreach ($ergebnis as $zeile) {
    echo htmlspecialchars($zeile['name'], ENT_QUOTES, 'UTF-8'), "\n";
}

Los desarrolladores deberían conocer dos límites. Los marcadores de posición no deben ir entre comillas; en las búsquedas con LIKE, los caracteres comodín % van en el valor que se pasa. Y los marcadores de posición solo sustituyen valores, no nombres de tablas ni de columnas. Cómo resolver con seguridad estos casos especiales lo muestra la guía Evitar la inyección SQL en PHP.

Tratamiento de errores: excepciones en lugar de errores silenciosos

Antes, ambas interfaces funcionaban por defecto de forma silenciosa: una consulta fallida devolvía false y quien no lo comprobaba seguía trabajando con datos vacíos. Eso ha cambiado:

  • PDO: desde PHP 8.0.0, PDO::ERRMODE_EXCEPTION es el valor por defecto. Los errores lanzan una PDOException.
  • mysqli: desde PHP 8.1.0, la notificación de errores está fijada en MYSQLI_REPORT_ERROR y MYSQLI_REPORT_STRICT. Los errores lanzan una mysqli_sql_exception; antes la notificación estaba desactivada.

Para la seguridad esto significa: las excepciones deben capturarse y registrarse, pero nunca deben llegar al navegador con texto SQL, nombres de tablas o credenciales. Cómo separarlo con limpieza lo describe la página Errores y excepciones en PHP.

Asignar bien credenciales y permisos

La base de datos solo está tan protegida como sus credenciales. Han demostrado su valor estas reglas:

  1. Usuario propio para cada aplicación: nunca utilizar el administrador de la base de datos para el sitio web.
  2. Solo los permisos necesarios: un sitio web suele necesitar leer, escribir, modificar y borrar en su propia base de datos, pero no permisos sobre otras bases de datos ni funciones del servidor.
  3. Sin conexión remota sin motivo: la base de datos solo debería ser accesible desde el servidor web, no desde Internet.
  4. Credenciales fuera de la raíz web: los archivos de configuración y .env no deben poder consultarse mediante URL. Más información en el artículo Proteger credenciales y archivos .env.
  5. Conexión cifrada con servidores separados: si la base de datos está en otro equipo, la conexión debería ir cifrada para que las credenciales y los contenidos no viajen en texto claro por la red.
  6. Accesos propios para el mantenimiento: las copias de seguridad, las evaluaciones y los administradores reciben usuarios propios. Así puede rastrearse en el registro quién hizo qué, y un acceso puede bloquearse sin perturbar el sitio web.

Bases de datos, PHP2Shell y el caso grave

PHP2Shell no afecta a la base de datos en sí, sino al intérprete de PHP: CVE-2026-109618 y CVE-2026-109619 permiten ejecutar código malicioso sin autenticación. Si lo consigue, el código se ejecuta con los permisos del proceso de PHP y, por tanto, puede leer por lo general el archivo de configuración con las credenciales de la base de datos. Las sentencias preparadas no ayudan frente a esto, porque el ataque no pasa por ninguna consulta SQL.

Por eso es obligatoria la actualización a PHP 8.5.12, 8.4.27, 8.3.36 u 8.2.35 y el reinicio de todos los procesos de PHP. Si hay indicios de un ataque con éxito, forman parte del proceso nuevas credenciales de la base de datos, una revisión de la base de datos en busca de contenidos introducidos y una valoración de la obligación de notificación según el RGPD. Aquí resulta útil una respuesta a incidentes estructurada.

Consejo

Cambie las contraseñas de la base de datos solo después de limpiar el servidor. Mientras se ejecute código malicioso, este puede volver a leer también las credenciales nuevas.

¿Cómo ayuda PHP SOS en concreto?

  • Comprobamos cómo accede su aplicación a la base de datos y si los usuarios, los permisos y el acceso de red se ajustan al mínimo necesario.
  • Verificamos si los archivos de configuración y de credenciales están fuera de la raíz web y no son accesibles públicamente.
  • Tras un incidente renovamos las credenciales en el orden correcto y revisamos la base de datos en busca de código introducido.
  • Documentamos los resultados de forma que los delegados de protección de datos y las aseguradoras puedan seguirlos.

Preguntas frecuentes

¿Debo usar PDO o mysqli?

Para proyectos nuevos, PDO suele ser la opción más flexible, porque funciona con muchas bases de datos mediante la misma interfaz. mysqli tiene sentido si utiliza exclusivamente MySQL o MariaDB y necesita sus funciones especiales. Ambos son seguros si se emplean de forma sistemática las sentencias preparadas.

¿Lanza PDO excepciones automáticamente ante los errores?

Sí, desde PHP 8.0.0 PDO::ERRMODE_EXCEPTION es el valor por defecto. Antes era el modo silencioso, en el que los errores solo podían consultarse mediante errorCode() y errorInfo(). Las aplicaciones antiguas suelen fijar todavía el modo de forma expresa.

¿Pueden los marcadores de posición sustituir también nombres de tablas o columnas?

No. Los marcadores de posición solo representan valores. Los nombres de tablas y columnas, por ejemplo para una ordenación, deben compararse con una lista fija de nombres permitidos antes de insertarse en la consulta.

¿Debo cambiar la contraseña de la base de datos después de PHP2Shell?

La actualización por sí sola no lo exige. Pero si hay indicios de que el servidor estuvo comprometido, todas las credenciales de los archivos de configuración se consideran conocidas. En ese caso, deberían renovarse tras la limpieza.

Fuentes

Contacto inmediato

Cada hora cuenta. Escríbanos ahora.

Describa brevemente el sistema, la versión de PHP y lo que ha observado. Nos pondremos en contacto, aclararemos la situación y trabajaremos en remoto hasta que el incidente quede cerrado y documentado.

Ayuda inmediata