Los sistemas de gestión de bases de datos (DBMS, por sus siglas en inglés) que trabajan con el lenguaje SQL son muy populares, pero también un blanco habitual de la ma­ni­pu­la­ción en el su­mi­ni­s­tro de datos. Así, por ejemplo, las entradas de usuario con un cifrado in­su­fi­cie­n­te y que contienen me­ta­ca­ra­c­te­res, como las comillas o el punto y coma, son una presa fácil para los ci­be­r­de­li­n­cue­n­tes. Una posible solución a este problema la brinda el uso de prepared sta­te­me­nts o se­n­te­n­cias pre­pa­ra­das, es decir, pla­n­ti­llas de in­s­tru­c­cio­nes a las bases de datos a las que no se asignan datos hasta el momento de su ejecución.

¿Qué tiene esta técnica de especial y en qué si­tua­cio­nes se usa? Como ejemplo, te mostramos cómo funcionan las prepared sta­te­me­nts en MySQL y cómo pueden usarse en la gestión de bases de datos.

¿Qué es una prepared statement?

Las prepared sta­te­me­nts, también llamadas consultas, comandos o se­n­te­n­cias pre­pa­ra­das, son pla­n­ti­llas para consultas a sistemas de bases de datos en lenguaje SQL cuyos pa­rá­me­tros están de­s­pro­vi­s­tos de valores. Para re­em­pla­zar dichos valores, estas pla­n­ti­llas trabajan con variables o ma­r­ca­do­res de posición, que no son su­s­ti­tui­dos por los valores reales hasta estar dentro del sistema. Cuando las consultas se in­tro­du­cen a mano, en cambio, los valores se asignan en el mismo momento de eje­cu­tar­las.

Todos los grandes sistemas de gestión de bases de datos que funcionan con SQL, como MySQL, MariaDB, Oracle, Microsoft SQL Server y Po­s­t­gre­S­QL, soportan prepared sta­te­me­nts, si bien la mayoría recurre para ello a un protocolo binario NoSQL. Algunos sistemas, entre los que se encuentra MySQL, también utilizan la sintaxis SQL co­n­ve­n­cio­nal a la hora de im­ple­me­n­tar las se­n­te­n­cias pre­pa­ra­das. Y algunos lenguajes de pro­gra­ma­ción, como Java, Perl, Python y PHP, prevén el uso de prepared sta­te­me­nts en sus bi­blio­te­cas estándar o sus ex­te­n­sio­nes. Si utilizas PHP para acceder a la base de datos, puedes im­ple­me­n­tar se­n­te­n­cias pre­pa­ra­das con la interfaz orientada a objetos PHP Data Objects (PDO) o con la extensión de PHP MySQLi.

¿Por qué conviene utilizar prepared sta­te­me­nts en MySQL y sistemas similares?

La principal razón para utilizar se­n­te­n­cias pre­pa­ra­das cuando se trabaja con sistemas de gestión de bases de datos como MySQL no es otra que la seguridad. El mayor problema de los métodos co­n­ve­n­cio­na­les de acceso a las bases de datos basadas en lenguaje SQL es la facilidad con la que pueden ser ma­ni­pu­la­das. Este tipo de ataques se denominan in­ye­c­cio­nes SQL: el código se completa o se modifica para conseguir acceso a datos sensibles o incluso lograr un control absoluto sobre la base de datos. En PHP y en lenguajes similares, las se­n­te­n­cias pre­pa­ra­das no dan lugar a tales lagunas en el sistema de seguridad, ya que no reciben valores concretos hasta que se ejecutan dentro del sistema.

Nota

Una condición para que una prepared statement o sentencia preparada sea realmente segura es que ninguno de sus co­m­po­ne­n­tes haya sido generado desde una fuente externa.

No obstante, la pro­te­c­ción contra las in­ye­c­cio­nes SQL no es el único argumento a favor de estas pla­n­ti­llas de so­li­ci­tu­des: una vez analizada y compilada, una prepared statement puede re­uti­li­zar­se en el sistema de la base de datos siempre que se quiera (variando los datos co­rre­s­po­n­die­n­tes cada vez). De este modo, cuando se trata de tareas en SQL que deben repetirse una y otra vez, las se­n­te­n­cias pre­pa­ra­das requieren muchos menos recursos y son más rápidas que las so­li­ci­tu­des manuales.

¿Cómo se utiliza exac­ta­me­n­te una prepared statement?

Sin entrar en los detalles de la sintaxis del lenguaje de pro­gra­ma­ción ni de las ca­ra­c­te­rí­s­ti­cas de cada sistema de gestión de base de datos, la in­co­r­po­ra­ción y el uso de se­n­te­n­cias pre­pa­ra­das suele dividirse en las si­guie­n­tes fases:

Fase 1: pre­pa­ra­ción

El primer paso es generar una plantilla de sentencia –en PHP, la función co­rre­s­po­n­die­n­te es prepare(). En lugar de los valores, a los pa­rá­me­tros re­le­va­n­tes se les asignan los ya me­n­cio­na­dos ma­r­ca­do­res de posición, también llamados pa­rá­me­tros de su­s­ti­tu­ción po­si­cio­na­les o variables bind. En general, estos ma­r­ca­do­res se ca­ra­c­te­ri­zan por un signo de in­te­rro­ga­ción (?), como en el ejemplo siguiente:

INSERT INTO Producto (Nombre, Precio) VALUES (?, ?);

Las se­n­te­n­cias pre­pa­ra­das ya completas se envían al sistema de gestión de bases de datos co­rre­s­po­n­die­n­te.

Fase 2: pro­ce­sa­mie­n­to de la plantilla en el DBMS

El sistema de gestión de bases de datos (DBMS) parsea, es decir, analiza si­n­tá­c­ti­ca­me­n­te la plantilla de sentencia para que en un siguiente paso se pueda compilar, es decir, co­n­ve­r­ti­r­se en una orden eje­cu­ta­ble. Durante este proceso, además, se optimiza la prepared statement.

Fase 3: ejecución

Más adelante, la plantilla procesada puede volver a uti­li­zar­se en el sistema de base de datos siempre que se quiera, con la única condición de que la apli­ca­ción o fuente de datos conectada pro­po­r­cio­ne el input adecuado, es decir, los datos que re­em­pla­za­rán a los ma­r­ca­do­res de posición. Retomando el código del ejemplo anterior (fase 1), al marcador o parámetro Nombre podría asi­g­nár­se­le el valor Libro y, al parámetro Precio, el valor 10; o también podría tratarse de un ordenador con el valor de precio 1000.

Tutorial: cómo utilizar prepared sta­te­me­nts en MySQL con MySQLi

Ahora que ya hemos explicado cómo funcionan las se­n­te­n­cias pre­pa­ra­das, en este tutorial ex­pli­ca­mos cómo usar estas pla­n­ti­llas con ejemplos concretos. Para ello, tomaremos:

  • MySQL como sistema de gestión de base de datos y
  • PHP como lenguaje de las prepared sta­te­me­nts.

Las versiones más recientes de MySQL soportan el uso de prepared sta­te­me­nts del lado del servidor basándose en un protocolo binario que contiene todas las órdenes SQL de ac­tua­li­za­ción de datos y que, además, registra todas las ac­tua­li­za­cio­nes desde la última vez que se guardaron los datos. Este tutorial toma como interfaz de acceso MySQLi, la extensión de PHP que también soporta se­n­te­n­cias pre­pa­ra­das por medio del protocolo binario.

Nota

Una buena y muy popular al­te­r­na­ti­va a MySQLi como API para prepared sta­te­me­nts es la interfaz orientada a objetos PDO (PHP Data Objects), que, por lo general, es más fácil de usar para los pri­n­ci­pia­n­tes.

PREPARE, EXECUTE y DEA­LLO­CA­TE PREPARE: las tres se­n­te­n­cias SQL básicas para utilizar una sentencia preparada

Existen tres comandos o se­n­te­n­cias SQL que tienen un papel decisivo en el uso de prepared sta­te­me­nts en bases de datos MySQL:

La sentencia PREPARE sirve para preparar, nunca mejor dicho, una prepared statement para su apli­ca­ción y, entre otras cosas, darle un nombre unívoco que sirva para recuperar y controlar la sentencia más adelante.

PREPARE stmt_name FROM preparable_stmt

Para ejecutar se­n­te­n­cias SQL ya pre­pa­ra­das se requiere el comando EXECUTE. Para indicar qué prepared statement en concreto se quiere ejecutar, se introduce el nombre generado con PREPARE. Ahora puedes decidir con qué fre­cue­n­cia quieres ejecutar una sentencia: puedes generar tantas variables distintas como quieras y asi­g­nar­les re­s­pe­c­ti­va­me­n­te tantos valores como quieras.

EXECUTE stmt_name
	[USING @var_name [, @var_name] ...]

Para anular una prepared statement en PHP se utiliza el comando DEA­LLO­CA­TE PREPARE. Las se­n­te­n­cias se borran también au­to­má­ti­ca­me­n­te al cerrar cada sesión. Esto es im­po­r­ta­n­te porque, de lo contrario, se al­ca­n­za­ría pronto el número máximo de se­n­te­n­cias es­ta­ble­ci­do por la variable max_prepared_stmt_count y ya no se podría crear ninguna sentencia preparada nueva.

{DEALLOCATE | DROP} PREPARE stmt_name

Se­n­te­n­cias SQL que se pueden usar como prepared sta­te­me­nts en MySQL

Prá­c­ti­ca­me­n­te todas las se­n­te­n­cias SQL apli­ca­bles a MySQL pueden pre­pa­rar­se y eje­cu­tar­se como prepared sta­te­me­nts. Una excepción son los llamados comandos de dia­g­nó­s­ti­co, que no pueden usarse como prepared sta­te­me­nts según el estándar SQL. En concreto, se trata de los si­guie­n­tes comandos:

  • SHOW WARNINGS
  • SHOW COUNT(*) WARNINGS
  • SHOW ERRORS
  • SHOW COUNT(*) ERRORS

Además, tampoco se pueden generar pla­n­ti­llas para so­li­ci­tu­des SQL referidas a las variables de sistema warning_count ni error_count.

Las se­n­te­n­cias o comandos que sí se pueden usar son los si­guie­n­tes:

ALTER TABLE
ALTER USER
ANALYZE TABLE
CACHE INDEX
CALL
CHANGE MASTER
CHECKSUM {TABLE | TABLES}
COMMIT
{CREATE | DROP} INDEX
{CREATE | RENAME | DROP} DATABASE
{CREATE | DROP} TABLE
{CREATE | RENAME | DROP} USER
{CREATE | DROP} VIEW
DELETE
DO
FLUSH {TABLE | TABLES | TABLES WITH READ LOCK | HOSTS | PRIVILEGES
	| LOGS | STATUS | MASTER | SLAVE | USER_RESOURCES}
GRANT
INSERT
INSTALL PLUGIN
KILL
LOAD INDEX INTO CACHE
OPTIMIZE TABLE
RENAME TABLE
REPAIR TABLE
REPLACE
RESET {MASTER | SLAVE}
REVOKE
SELECT
SET
SHOW {WARNINGS | ERRORS}
SHOW BINLOG EVENTS
SHOW CREATE {PROCEDURE | FUNCTION | EVENT | TABLE | VIEW}
SHOW {MASTER | BINARY} LOGS
SHOW {MASTER | SLAVE} STATUS
SLAVE {START | STOP}
TRUNCATE TABLE
UNINSTALL PLUGIN
UPDATE

Pa­r­ti­cu­la­ri­da­des de la sintaxis SQL de las prepared sta­te­me­nts en PHP

Al co­m­pa­rar­la con la sintaxis SQL estándar, la sintaxis de una sentencia preparada tiene algunas pa­r­ti­cu­la­ri­da­des que no pueden pasarse por alto. En primer lugar, destaca el uso de ma­r­ca­do­res de posición para los valores de los pa­rá­me­tros, gracias a los cuales las se­n­te­n­cias pre­pa­ra­das ofrecen una gran ventaja en lo que al acceso a los sistemas de gestión de bases de datos se refiere. A partir de MySQL 8.0, estos ma­r­ca­do­res de posición también pueden usarse, por ejemplo, en los pa­rá­me­tros OUT e INOUT, en las se­n­te­n­cias PREPARE y EXECUTE. En el caso de los pa­rá­me­tros IN, incluso están di­s­po­ni­bles con in­de­pe­n­de­n­cia del sistema de la base de datos. Otras ca­ra­c­te­rí­s­ti­cas es­pe­cí­fi­cas de la sintaxis de las prepared sta­te­me­nts son las si­guie­n­tes:

  • La sintaxis SQL de las prepared sta­te­me­nts en PHP no permite ani­da­cio­nes. Por lo tanto, una sentencia a la que se haya referido un comando PREPARE no podrá ser ella misma PREPARE, EXECUTE ni DEA­LLO­CA­TE PREPARE.
  • Las prepared sta­te­me­nts pueden usarse en pro­ce­di­mie­n­tos al­ma­ce­na­dos (función para solicitar procesos completos de comandos).
  • Las llamadas se­n­te­n­cias múltiples, que son varias se­n­te­n­cias enviadas de una vez, no son posibles dentro de una sentencia preparada ni dentro de una cadena de ca­ra­c­te­res con se­pa­ra­ción mediante punto y coma.

Prepared sta­te­me­nts en MySQLi: ejemplo

El siguiente ejemplo muestra qué forma tiene una entrada en MySQLi con prepared sta­te­me­nts en PHP:

<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// Establecer conexión 
$conn = new mysqli($servername, $username, $password, $dbname);
// Comprobar conexión 
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
// Preparación de las prepared statements
$stmt = $conn->prepare("INSERT INTO MisClientes (Nombre, Apellido, Correo) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $nombre, $apellido, $correo);
// Asignación de parámetros y ejecución
$Nombre = "Alicia";
$Apellido = "Torres";
$Correo = "aliciaperez@ejemplo.com";
$stmt->execute();
$Nombre = "Juan";
$Apellido = "Martínez";
$Correo= "juanmartinez@ejemplo.com";
$stmt->execute();
$Nombre = "Ana";
$Apellido = "López";
$Correo = "analopez@ejemplo.com";
$stmt->execute();
echo "Nuevas entradas añadidas con éxito";
$stmt->close();
$conn->close();
?>

Esta secuencia de comandos PHP establece en primer lugar la conexión con la base de datos MySQL ($conn), en la cual hay que indicar los datos del servidor en cuestión, como el nombre de equipo, el nombre de usuario, la co­n­tra­se­ña y el nombre de la base de datos.

Con la línea “INSERT INTO Mi­s­Clie­n­tes (Nombre, Apellido, Correo) VALUES (?, ?, ?)” comienza la parte decisiva de la sentencia preparada: la base de datos de clientes Mi­s­Clie­n­tes deberá recibir input (INSERT INTO) en cada uno de los pa­rá­me­tros Nombre, Apellido y Correo. Los valores (VALUES) serán re­em­pla­za­dos en primer lugar por ma­r­ca­do­res de posición, que se pueden reconocer por el signo de in­te­rro­ga­ción (?).

A co­n­ti­nua­ción, hay que enlazar los pa­rá­me­tros (bind_param). Para ello, la base de datos necesita in­fo­r­ma­ción acerca del tipo de datos que tratará. El argumento usado con este fin en el ejemplo, sss, muestra que los tres pa­rá­me­tros son strings, es decir, cadenas de ca­ra­c­te­res. Otras al­te­r­na­ti­vas de enlace serían las si­guie­n­tes:

  • i: INTEGER (número entero)
  • d: DOUBLE (valor numérico apro­xi­ma­do)
  • b: BLOB (gran objeto binario de datos)
aN5KqxK1slc.jpg Para mostrar este video, se requieren cookies de terceros. Puede acceder y cambiar sus ajustes de cookies aquí.
Ir al menú principal