Consultas seguras en MySQL con PHP

3 de Octubre - en PHP

Uno de los principales problemas al trabajar con bases de datos, es verificar los datos que el usuario ingresa y de este modo evitar ataques de tipo inyección SQL o que puedan causar problemas al generar la consulta, tradicionalmente se utilizaban métodos como escapar los datos o eliminar palabras especificas usadas en la sintaxis de SQL, lo cual era un proceso complicado y no confiable al 100%. Todo esto se soluciona utilizado sentencias preparadas, de este modo la sintaxis de nuestra petición y los datos viajan separados.

Introducción

Cuando realizamos una consulta lo que realmente hacemos es crear un pequeño script el cual luego es ejecutado por la base de datos ya sea MYSQL/MariaDB u otro, por lo que incluir directamente los datos provenientes del usuario sin ningún tipo de análisis es peligroso, ya que estos podrían contener caracteres o palabras específicas que rompan nuestra consulta.

Inicialmente una de las soluciones era escapar los datos, en donde se reemplazan ciertos caracteres que pudiesen causar problemas en la consulta, lo cual ocasionaba que el tamaño de los datos a salvar incrementara. La solución más adecuada es utilizar sentencias separadas, donde la consulta y los datos de esta viajaran de forma separa, de esta forma la consulta a la base de datos será segura.

Sentencias preparadas en PHP

Par este ejemplo utilizaremos una base de datos llamada “mydb”, la cual contendrá una tabla llamada “usuarios” con las siguientes columnas: nombre, edad y teléfono. Donde realizaremos una consulta tipo SELECT para obtener los teléfonos que coincidan con el nombre y la edad especifica.

Iniciaremos preparando la conexión con nuestra base de datos, para este ejemplo utilizaremos MySQL:

//Configurando la conexion
$db = new mysqli($db_host, $db_usuario, $db_password, $db_nombre);

//Verifiando el estado de la conexion
if(mysqli_connect_errno()) {
    exit("Error al conectar con la BD.");
}

//Seleccionamos el set de caracteres
mysqli_set_charset($db, "utf8");

Preparando la consulta

Utilizaremos la función prepare, la cual contendrá nuestra consulta, pero reemplazando los valores de los datos con el signo ?:

//preparamos la consulta reemplazando los datos por el signo ?
$pr = $db->prepare("SELECT telefono FROM usuarios WHERE nombre=? AND edad=?");

Para indicar los valores utilizaremos la función bind_param, la cual recibe 2 parámetros, el primero donde indicaremos el tipo de dato con una letra, el segundo es el conjunto de valores pasados por referencia.

$nombre = 'jose';
$edad = 18;

//Indicamos los valores pasados por referencia
$pr->bind_param("si", $nombre, $edad);

Tipo de datos: indicaremos con una letra el tipo de datos en el mismo orden en el cual se agregaran en el conjunto de valores pasados por referencia, los tipos de datos validos son:

i: Integer

s: String

d: Double

b: Blob

En este caso el primer valor “nombre” es de tipo String y el segundo “edad” es de tipo Integer, por lo tanto lo indicaremos como “si”, cabe señalar que los datos pasados por bind_param son por referencia por lo tanto es necesario indicar una variable y no el valor directo.

Ejecutamos la consulta con la función execute, la cual nos devolverá un valor booleano con el estado de nuestra consulta, en caso de un error usaremos el valor error, para más detalles.

//Ejecutamos la consulta
if($pr->execute()){
	
	//Alamacenaos los datos de la consulta
	$pr->store_result();
	
	if($pr->num_rows == 0){		
		echo "Sin resultados";
	}
	
	//Indicamos la variable donde se guardaran los resultados
	$pr->bind_result($telefono);
	
	//listamos todos los resultados
	while($pr->fetch()){
		echo "Telefono: $telefono <br>";
	}
	
	//Cerramos la conexion
	$pr->close();
	
} else {
	exit('Error al realizar la consulta: '.$pr->close());
}

Verificaremos con el valor num_rows, si la consulta devolvió algún valor, si es así usaremos la función bind_result para indicar donde se almacenaran los resultados, en este caso es un valor por referencia, seguiremos el mismo orden como indicamos en la consulta, estos valores se irán actualizando conforme ejecutemos la función  fetch.

Finalmente utilizaremos la función fetch para volcar los datos, como en este caso esperamos más de un posible valor, usáramos la función en un ciclo while mientras esta retorne un valor.

Insertar nuevas filas

En este ejemplo usaremos INSERT para insertaremos una nueva fila con los datos nombre, edad y teléfono, debido a que estos datos provendrán del usuario aun es necesario verificar que cumplan con las condiciones del tipo de dato y longitud.

//Insetando nuevos datos

//Configurando la conexion
$db = new mysqli("localhost", "root", "root", "mydb");
if(mysqli_connect_errno())  exit("Error al conectar con la BD.");
mysqli_set_charset($db, "utf8");

//preparamos la consulta INSERT reemplazando los datos por el signo ?
$pr = $db->prepare("INSERT INTO usuarios(nombre, edad, telefono)VALUES(?,?,?)");

//Datos provenientes del usuario
$nombre = 'maria';
$edad = 20;
$telefono = "123456";

//Indicamos el tipo de datos y pasamos los valores por referencia
$pr->bind_param("sis", $nombre, $edad, $telefono);


if($pr->execute()){
	
	//Datos insertados...
	
	echo 'Datos agregados correctamente.';
	
	$pr->close();
	
} else {
	echo 'Error al realizar la consulta: '.$pr->error;
	$pr->close();
}

Actualizando una fila

En este ejemplo actualizaremos el valor de la columna “teléfono” con UPDATE, en las filas que coincidan con el nombre y edad específica, además usaremos affected_rows para determinar el número de filas actualizadas.

//Modificando una fila

//Configurando la conexion
$db = new mysqli("localhost", "root", "root", "mydb");
if(mysqli_connect_errno())  exit("Error al conectar con la BD.");
mysqli_set_charset($db, "utf8");

//preparamos la consulta reemplazando los datos por el signo ?
$pr = $db->prepare("UPDATE usuarios SET telefono=? WHERE nombre=? AND edad=?");

//Valor actualizado
$telefono = "000";

//Valores a coincidir
$nombre = 'jose';
$edad = 18;

//Indicamos el tipo de datos y pasamos los valores por referencia
$pr->bind_param("ssi", $telefono, $nombre, $edad);

//Ejecutamos la consulta
if($pr->execute()){
	
	//Veficiando si alguna fila fue actualizada
	if($pr->affected_rows==0){
		echo "Ninguna fila fue actualizada: ";
	} else
		echo "Datos actualizados correctamente.";
		
	$pr->close();

} else {
	echo 'Error al realizar la consulta: '.$pr->error;
	$pr->close();
}

 

Ejemplo

Descargar
Etiquetas
PHP php mysql
Compartir