php - que - pdo mysql
¿Por qué PDO es mejor para escapar de consultas MySQL/querystrings que mysql_real_escape_string? (6)
Me han dicho que sería mejor usar PDO
para escapar de MySQL, en lugar de mysql_real_escape_string
.
Tal vez estoy teniendo un día de muerte cerebral (o puede ser el hecho de que no soy de ninguna manera un programador natural, y todavía estoy en la etapa de novato cuando se trata de PHP), pero teniendo revisé el manual de PHP y leí la entrada en PDO , todavía no estoy más claro en cuanto a qué PDO realmente es y por qué es mejor que usar mysql_real_escape_string
. Esto puede deberse a que aún no me he familiarizado con las complejidades de OOP (supongo que tiene algo que ver con OOP), pero aparte del hecho de que las variables y los valores de matriz parecen tener dos puntos delante de ellos, Todavía no estoy seguro de qué es realmente y cómo lo usas (y por qué es mejor que mysql_real_escape_string
. (También puede tener algo que ver con el hecho de que realmente no entiendo muy bien qué son las "clases"). , así que cuando leí "clase PDO" no soy realmente más sabio).
Después de leer un artículo o dos en el bit ''Zona de desarrollador'' del sitio web de MySQL, todavía no estoy más claro. Como ni siquiera puedo descifrar qué es en este momento, creo que probablemente esté un poco más allá de mí en este momento, pero todavía estoy interesado en ampliar mi educación y descubrir cómo puedo mejorar las cosas.
¿Alguien podría explicarme en ''llanura inglesa'' qué es PDO (o indicarme algo sobre el tema escrito en inglés sencillo) y cómo podría usarlo?
¿Por qué PDO es mejor para escapar de consultas MySQL / querystrings que mysql_real_escape_string?
Simplemente porque "escaparse" solo no tiene sentido.
Además, son asuntos incomparables diferentes.
El único problema con escapar es que todos se equivocan, asumiendo que es una especie de "protección".
Todos dicen "escapé de mis variables" con el significado "protegí mi consulta".
Aunque escapar solo no tiene nada que ver con la protección.
La protección se puede lograr de forma aproximada en caso de que haya escapado y haya citado mis datos , pero no es aplicable en todas partes, por ejemplo, para los identificadores (además de PDO, por cierto).
Entonces, la respuesta es:
- PDO, cuando se escapa para los valores combinados, se aplica no solo al escape sino también a las citas, por eso es mejor.
- "escaparse" no es sinónimo de "protección". "escapar + cotizar" aproximadamente es.
- pero para algunas partes de consulta ambos métodos son inaplicables.
A diferencia de mysql_real_escape_string, PDO le permite aplicar un tipo de datos.
<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = ''red'';
$sth = $dbh->prepare(''SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour'');
$sth->bindParam('':calories'', $calories, PDO::PARAM_INT);
$sth->bindParam('':colour'', $colour, PDO::PARAM_STR, 12);
$sth->execute();
?>
Tenga en cuenta que en el ejemplo anterior, se requiere que el primer parámetro, calorías, sea un número entero (PDO :: PARAM_INT).
En segundo lugar, para mí, las consultas parametrizadas PDO son más fáciles de leer. Prefiero leer:
SELECT name FROM user WHERE id = ? AND admin = ?
que
SELECT name FROM user WHERE id = mysql_real_escape_string($id) AND admin = mysql_real_escape_string($admin);
En tercer lugar, no tiene que asegurarse de citar los parámetros correctamente. PDO se ocupa de eso. Por ejemplo, mysql_real_query_string:
SELECT * FROM user WHERE name = ''mysql_real_escape_string($name)'' //note quotes around param
vs
SELECT * FROM user WHERE name = ?
Finalmente, PDO le permite transferir su aplicación a un db diferente sin cambiar sus llamadas de datos PHP.
Además de evitar la inyección SQL, PDO le permite preparar una consulta una vez y ejecutarla varias veces. Si su consulta se ejecuta varias veces (dentro de un bucle, por ejemplo), este método debería ser más eficiente (digo "debería ser", porque parece que no siempre es el caso en versiones anteriores de MySQL). El método de preparación / vinculación también está más en línea con otros idiomas con los que he trabajado.
Como las respuestas actuales entran en detalles mientras su pregunta está más dirigida a una descripción general, lo intentaré:
Las clases PDO pretenden encapsular toda la funcionalidad necesaria para interactuar con una base de datos. Lo hacen definiendo ''métodos'' (OO salón para funciones) y ''propiedades'' (salón OO para variables). Los usarías como un reemplazo completo para todas las funciones ''estándar'' que estás usando ahora para hablar con una base de datos.
Entonces, en lugar de llamar una serie de las funciones ''mysql_doSomething ()'', almacenando sus resultados en sus propias variables, ''instanciaría'' un objeto de la clase PDO (''clase'' = definición abstracta, ''objeto'' = instancia concreta utilizable) de una clase) y llamar a métodos en ese objeto para hacer lo mismo.
Como ejemplo, sin PDO, harías algo como esto:
// Get a db connection
$connection = mysql_connect(''someHost/someDB'', ''userName'', ''password'');
// Prepare a query
$query = "SELECT * FROM someTable WHERE something = " . mysql_real_escape_string($comparison) . "''";
// Issue a query
$db_result = mysql_query($query);
// Fetch the results
$results = array();
while ($row = mysql_fetch_array($db_result)) {
$results[] = $row;
}
mientras que esto sería el equivalente usando PDO:
// Instantiate new PDO object (will create connection on the fly)
$db = new PDO(''mysql:dbname=someDB;host=someHost'');
// Prepare a query (will escape on the fly)
$statement = $db->prepare(''SELECT * FROM someTable WHERE something = :comparison'');
// $statement is now a PDOStatement object, with its own methods to use it, e.g.
// execute the query, passing in the parameters to replace
$statement->execute(array('':comparison'' => $comparison));
// fetch results as array
$results = $statement->fetchAll();
Entonces, a primera vista, no hay mucha diferencia, excepto en sintaxis. Pero la versión PDO tiene algunas ventajas, la más importante es la independencia de la base de datos:
Si necesita hablar con una base de datos PostgreSQL, solo cambiaría mysql:
a pgsql:
en la instancia de llamada new PDO()
. Con el método anterior, tendrías que pasar por todo tu código, reemplazando todas las funciones ''mysql_doSomething ()'' con su contraparte ''pg_doSomthing ()'' (siempre buscando posibles diferencias en el manejo de los parámetros). Lo mismo ocurriría con muchos otros motores de bases de datos compatibles.
Entonces, para volver a su pregunta, PDO básicamente le ofrece una manera diferente de lograr las mismas cosas, al tiempo que ofrece algunos accesos directos / mejoras / ventajas. Por ejemplo, el escaparse sucedería automáticamente de la manera apropiada necesaria para el motor de base de datos que está utilizando. También la sustitución de parámetros (evita inyecciones de SQL, que no se muestran en el ejemplo) es mucho más fácil, por lo que es menos propenso a errores.
Deberías leer algunos conceptos básicos de POO para tener una idea de otras ventajas.
No estoy muy familiarizado con PDO, pero hay una distinción entre "declaraciones preparadas" y cadenas escapadas. El escape consiste en eliminar cadenas de caracteres no permitidas de la consulta, pero las declaraciones preparadas consisten en indicarle a la base de datos qué tipo de consulta esperar .
Una consulta tiene varias partes
Piénselo de esta manera: cuando realiza una consulta a la base de datos, le está diciendo varias cosas por separado. Una cosa podría ser, por ejemplo, "Quiero que selecciones". Otro podría ser "limitarlo a las filas DONDE el nombre de usuario es el siguiente valor".
Si crea una consulta como una cadena y la entrega a la base de datos, no conoce ninguna parte hasta que obtiene la cadena completa. Puedes hacer esto:
''SELECT * FROM transactions WHERE username=$username''
Cuando recibe esa cadena, tiene que analizarla y decidir "esto es un SELECT
con un WHERE
".
Cómo mezclar las partes
Supongamos que un usuario malintencionado billysmith OR 1=1
su nombre de usuario como billysmith OR 1=1
. Si no tiene cuidado, puede poner eso en su cadena, lo que resulta en:
''SELECT * FROM transactions WHERE username=billysmith OR 1=1''
... que devolvería todas las transacciones para todos los usuarios , porque 1 siempre equivale a 1. ¡Vaya, te han pirateado!
¿Ves lo que pasó? La base de datos no sabía qué partes esperar en su consulta , por lo que solo analizó la cadena. No sorprendió que el WHERE
tuviera un OR
, con dos condiciones que pudieran satisfacerlo.
Manteniendo las partes derechas
Si solo hubiera sabido qué esperar , es decir, un SELECT
WHERE
solo tenía una condición, el usuario malicioso no podría haberla engañado.
Con una declaración preparada, puede darle esa expectativa correcta. Usted puede decirle a la base de datos "Estoy a punto de enviarle un SELECT
, y se limitará a las filas WHERE username =
una cadena que estoy a punto de darle. Eso es todo, no hay otras partes de la consulta . ¿Estás listo? OK, aquí viene la cadena para comparar con el nombre de usuario ".
Con esa expectativa, la base de datos no se dejaría engañar: solo devolvería filas donde la columna de username
contiene la cadena real ''billysmith O 1 = 1''. Si nadie tiene ese nombre de usuario, no devolverá nada.
Otros beneficios de declaraciones preparadas
Además de los beneficios de seguridad, las declaraciones preparadas tienen un par de ventajas de velocidad:
- Se pueden reutilizar con diferentes parámetros, lo que debería ser más rápido que crear una nueva consulta desde cero, porque la base de datos ya sabe básicamente lo que está por pedir. Ya ha construido su "plan de consulta".
- Algunas bases de datos (Postgres es una, creo) comenzarán a hacer un plan de consulta tan pronto como obtengan la declaración preparada, antes de que realmente hayas enviado los parámetros para usar con ella. Por lo tanto, es posible que vea una aceleración incluso en la primera consulta.
Para otra explicación, vea la respuesta de Theo here .
imagina que escribes algo como:
$query = ''SELECT * FROM table WHERE id = '' . mysql_real_escape_string($id);
esto no lo salvará de las inyecciones, porque $ id podría ser 1 OR 1=1
y obtendrá todos los registros de la tabla. tendrías que convertir $ id al tipo de datos correcto (int en ese caso)
El pdo tiene otra ventaja, y esa es la intercambiabilidad de los backends de la base de datos.