Pregunta ¿Por qué no debería usar mysql_ * funciones en PHP?


¿Cuáles son las razones técnicas por las que uno no debería usar mysql_* funciones? (p.ej. mysql_query(), mysql_connect() o mysql_real_escape_string())?

¿Por qué debería usar algo más, incluso si trabajan en mi sitio?

Si no funcionan en mi sitio, ¿por qué obtengo errores como

Advertencia: mysql_connect (): no existe tal archivo o directorio


2194
2017-10-12 13:18


origen


Respuestas:


La extensión MySQL:

  • No está bajo desarrollo activo
  • Es oficialmente obsoleto a partir de PHP 5.5 (lanzado en junio de 2013).
  • Ha sido remoto enteramente a partir de PHP 7.0 (lanzado en diciembre de 2015)
    • Esto significa que a partir de 31 de diciembre de 2018 no existirá en ninguna versión soportada de PHP. Actualmente, solo se pone seguridad actualizaciones.
  • Carece de una interfaz OO
  • No es compatible:
    • Consultas asincrónicas sin bloqueo
    • Declaraciones preparadas o consultas parametrizadas
    • Procedimientos almacenados
    • Múltiples declaraciones
    • Actas
    • El "nuevo" método de autenticación de contraseña (activado por defecto en MySQL 5.6; requerido en 5.7)
    • Toda la funcionalidad en MySQL 5.1

Como está obsoleto, su uso hace que su código sea menos resistente al futuro.

La falta de soporte para las declaraciones preparadas es particularmente importante ya que proporcionan un método más claro y menos propenso a errores para escaparse y citar datos externos que escaparse manualmente con una llamada de función separada.

Ver la comparación de extensiones SQL.


1808
2018-01-01 11:52



PHP ofrece tres API diferentes para conectarse a MySQL. Estos son los mysql(eliminado a partir de PHP 7), mysqliy PDO extensiones

los mysql_* las funciones solían ser muy populares, pero su uso ya no se fomenta. El equipo de documentación está discutiendo la situación de seguridad de la base de datos, y educar a los usuarios para que se alejen de la extensión ext / mysql comúnmente utilizada es parte de esto (verifique php.internals: deprecating ext / mysql)

Y el posterior equipo desarrollador de PHP tomó la decisión de generar E_DEPRECATED errores cuando los usuarios se conectan a MySQL, ya sea a través de mysql_connect(), mysql_pconnect() o la funcionalidad de conexión implícita incorporada ext/mysql.

ext/mysql estaba oficialmente desaprobado a partir de PHP 5.5 y ha sido eliminado a partir de PHP 7.

Ver el cuadro rojo?

Cuando vas a cualquier mysql_* página del manual de funciones, verá un recuadro rojo, explicando que ya no se debe usar.

Por qué


Alejándose de ext/mysql no solo se trata de seguridad, sino también de tener acceso a todas las características de la base de datos MySQL.

ext/mysql fue construido para MySQL 3.23 y solo obtuvo muy pocas adiciones desde entonces, mientras que mayormente mantuvo la compatibilidad con esta versión anterior, lo que hace que el código sea un poco más difícil de mantener. Faltan funciones que no son compatibles con ext/mysql incluir:del manual de PHP)

Motivo para no usar mysql_* función:

  • No bajo desarrollo activo
  • Eliminado a partir de PHP 7
  • Carece de una interfaz OO
  • No es compatible con consultas asincrónicas sin bloqueo
  • No admite declaraciones preparadas o consultas parametrizadas
  • No es compatible con procedimientos almacenados
  • No es compatible con múltiples declaraciones
  • No es compatible actas
  • No admite todas las funciones en MySQL 5.1

Por encima del punto citado de la respuesta de Quentin

La falta de soporte para las declaraciones preparadas es particularmente importante, ya que proporcionan un método más claro y menos propenso a errores para escaparse y citar datos externos que escaparse manualmente con una llamada de función separada.

Ver el comparación de extensiones SQL.


Suprimir las advertencias de deprecation

Mientras el código se convierte a MySQLi/PDO, E_DEPRECATED los errores pueden ser suprimidos estableciendo error_reporting en php.ini excluir E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Tenga en cuenta que esto también se ocultará otras advertencias de deprecation, que, sin embargo, puede ser para cosas que no sean MySQL. (del manual de PHP)

El artículo PDO vs. MySQLi: ¿Cuál debería usar? por Dejan Marjanovic te ayudará a elegir.

Y una mejor manera es PDO, y ahora estoy escribiendo un simple PDO tutorial.


Un tutorial simple y breve de PDO


P. La primera pregunta en mi mente fue: ¿qué es `PDO`?

UN. "PDO - Objetos de datos PHP - es una capa de acceso a la base de datos que proporciona un método uniforme de acceso a múltiples bases de datos ".

alt text


Conectando a MySQL

Con mysql_* función o podemos decirlo de la manera antigua (obsoleto en PHP 5.5 y superior)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

Con PDO: Todo lo que necesitas hacer es crear un nuevo PDO objeto. El constructor acepta parámetros para especificar la fuente de la base de datos PDOEl constructor mayormente toma cuatro parámetros que son DSN (nombre de fuente de datos) y opcionalmente username, password.

Aquí creo que estás familiarizado con todos, excepto DSN; esto es nuevo en PDO. UN DSN es básicamente una cadena de opciones que dicen PDO qué controlador usar y detalles de conexión. Para mayor referencia, verifique PDO MySQL DSN.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

Nota: también puedes usar charset=UTF-8, pero a veces causa un error, por lo que es mejor usar utf8.

Si hay algún error de conexión, lanzará un PDOException objeto que se puede atrapar para manejar Exception promover.

Buena lectura: Conexiones y administración de conexiones ¶ 

También puede pasar varias opciones de controlador como una matriz al cuarto parámetro. Recomiendo pasar el parámetro que pone PDO en modo de excepción. Porque algunos PDO los controladores no son compatibles con las declaraciones nativas preparadas, por lo que PDO realiza la emulación de la preparación. También le permite activar manualmente esta emulación. Para usar las instrucciones preparadas del lado del servidor nativo, debe establecerlo explícitamente false.

La otra es apagar la emulación de preparación que está habilitada en el MySQLcontrolador de forma predeterminada, pero la emulación de preparación se debe desactivar para usar PDO sin peligro.

Más adelante explicaré por qué se debe desactivar la emulación de preparación. Para encontrar el motivo, compruebe esta publicación.

Solo se puede usar si está usando una versión anterior de MySQL que no recomiendo

A continuación hay un ejemplo de cómo puedes hacerlo:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

¿Podemos establecer atributos después de la construcción de PDO?

, también podemos establecer algunos atributos después de la construcción de PDO con el setAttribute método:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Manejo de errores


El manejo de errores es mucho más fácil en PDO que mysql_*.

Una práctica común cuando se usa mysql_* es:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die() no es una buena manera de manejar el error ya que no podemos manejar la cosa en die. Simplemente terminará el guión abruptamente y luego repetirá el error en la pantalla que normalmente NO desea mostrar a sus usuarios finales, y permitirá que los hackers descubran su esquema. Alternativamente, los valores de retorno de mysql_* funciones a menudo se pueden utilizar en conjunción con Error de MySQL() para manejar errores

PDO ofrece una mejor solución: excepciones. Cualquier cosa que hagamos con PDO debe ser envuelto en una try-catch bloquear. Podemos forzar PDO en uno de los tres modos de error configurando el atributo de modo de error. Tres modos de manejo de errores están debajo.

  • PDO::ERRMODE_SILENT. Simplemente está configurando códigos de error y actúa de la misma manera que mysql_* donde debes verificar cada resultado y luego mirar $db->errorInfo(); para obtener los detalles del error.
  • PDO::ERRMODE_WARNING Aumento E_WARNING. (Advertencias de tiempo de ejecución (errores no fatales). La ejecución del script no se detiene).
  • PDO::ERRMODE_EXCEPTION: Lanzar excepciones. Representa un error planteado por PDO. No deberías lanzar un PDOException de tu propio código Ver Excepciones para más información sobre excepciones en PHP. Actúa de manera muy similar or die(mysql_error());cuando no está atrapado Pero a diferencia or die(), el PDOException puede capturarse y manejarse con gracia si elige hacerlo.

Buena lectura:

Me gusta:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

Y puedes envolverlo try-catch, como abajo:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

No tienes que manejar con try-catch ahora mismo. Puede atraparlo en cualquier momento apropiado, pero le recomiendo que use try-catch. También puede tener más sentido atraparlo fuera de la función que llama al PDO cosas:

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

Además, puedes manejar por or die() o podemos decir como mysql_*, pero será muy variado Puede ocultar los mensajes de error peligrosos en producción girando display_errors off y solo leyendo tu registro de errores

Ahora, después de leer todas las cosas anteriores, probablemente estés pensando: ¿qué diablos es eso cuando solo quiero comenzar a inclinarme de forma simple? SELECT, INSERT, UPDATE, o DELETE declaraciones? No te preocupes, aquí vamos:


Seleccionar datos

PDO select image

Entonces, ¿qué estás haciendo en mysql_* es:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

Ahora en PDO, puedes hacer esto como:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

O

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

Nota: Si está utilizando el método como a continuación (query()), este método devuelve un PDOStatement objeto. Entonces, si quieres buscar el resultado, úsalo como se indica arriba.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

En PDO Data, se obtiene a través de ->fetch(), un método de manejo de tu estado de cuenta. Antes de invocar fetch, el mejor enfoque sería decirle a PDO cómo desea que se obtengan los datos. En la siguiente sección, estoy explicando esto.

Fetch Modes

Tenga en cuenta el uso de PDO::FETCH_ASSOC en el fetch() y fetchAll() codigo arriba. Esto dice PDO para devolver las filas como una matriz asociativa con los nombres de campo como claves. También hay muchos otros modos de búsqueda que explicaré uno por uno.

Antes que nada, explico cómo seleccionar el modo de búsqueda:

 $stmt->fetch(PDO::FETCH_ASSOC)

En el de arriba, he estado usando fetch(). También puedes usar:

Ahora voy a buscar el modo:

  • PDO::FETCH_ASSOC: devuelve una matriz indexada por nombre de columna como se devuelve en su conjunto de resultados
  • PDO::FETCH_BOTH (predeterminado): devuelve una matriz indexada por el nombre de columna y el número de columna indexada por 0 como se devuelve en su conjunto de resultados

¡Hay incluso más opciones! Lea sobre todos ellos en PDOStatement Obtener documentación.

Obtener el recuento de filas:

En lugar de usar mysql_num_rows para obtener el número de filas devueltas, puede obtener un PDOStatement y hacer rowCount(), me gusta:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

Obteniendo la última ID insertada

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

Insertar y actualizar o eliminar declaraciones

Insert and update PDO image

Qué estamos haciendo en mysql_* la función es:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

Y en PDO, esto mismo se puede hacer de la siguiente manera:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

En la consulta anterior PDO::exec ejecutar una instrucción SQL y devuelve el número de filas afectadas.

Insertar y eliminar se tratarán más adelante.

El método anterior solo es útil cuando no se usa variable en la consulta. Pero cuando necesite usar una variable en una consulta, nunca intente como lo anterior y para estado preparado o enunciado parametrizado es.


Declaraciones preparadas

Q. ¿Qué es una declaración preparada y por qué las necesito?
A. Una declaración preparada es una declaración de SQL precompilada que se puede ejecutar varias veces enviando solo los datos al servidor.

El flujo de trabajo típico de usar una declaración preparada es el siguiente (citado de Wikipedia tres 3 puntos)

  1. Preparar: La aplicación crea la plantilla de declaración y la envía al sistema de gestión de base de datos (DBMS). Ciertos valores se dejan sin especificar, llamados parámetros, marcadores de posición o variables de vinculación (etiquetados ? abajo):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. El DBMS analiza, compila y realiza la optimización de la consulta en la plantilla de la declaración, y almacena el resultado sin ejecutarlo.

  3. Ejecutar: En un momento posterior, la aplicación proporciona (o enlaza) valores para los parámetros, y el DBMS ejecuta la declaración (posiblemente devolviendo un resultado). La aplicación puede ejecutar la declaración tantas veces como quiera con diferentes valores. En este ejemplo, podría suministrar 'Pan' para el primer parámetro y 1.00para el segundo parámetro

Puede usar una declaración preparada incluyendo marcadores de posición en su SQL. Básicamente, hay tres unidades sin marcadores de posición (no intente esto con la variable que está arriba), una con marcadores de posición sin nombre y otra con marcadores de posición con nombre.

Q. Entonces, ¿qué son los marcadores de posición y cómo los uso?
A. Marcadores de posición con nombre. Use nombres descriptivos precedidos por dos puntos, en lugar de signos de interrogación. No nos importa la posición / orden del valor en el titular del lugar del nombre:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

También puede vincular usando una matriz de ejecución:

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Otra buena característica para OOP amigos es que los marcadores de posición tienen la capacidad de insertar objetos directamente en su base de datos, suponiendo que las propiedades coinciden con los campos nombrados. Por ejemplo:

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q. Entonces, ¿qué son marcadores de posición sin nombre y cómo los uso?
A. Veamos un ejemplo:

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

y

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

En lo anterior, puedes ver esos ? en lugar de un nombre como en un titular de lugar de nombre. Ahora en el primer ejemplo, asignamos variables a los diversos marcadores de posición ($stmt->bindValue(1, $name, PDO::PARAM_STR);) Luego, asignamos valores a esos marcadores de posición y ejecutamos la declaración. En el segundo ejemplo, el primer elemento de matriz va al primero ? y el segundo a la segunda ?.

NOTA: En marcadores de posición sin nombre debemos cuidar el orden correcto de los elementos en la matriz que estamos pasando a la PDOStatement::execute() método.


SELECT, INSERT, UPDATE, DELETE consultas preparadas

  1. SELECT:

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

NOTA:

sin embargo PDO y / o MySQLi no son completamente seguros. Verifica la respuesta ¿Las declaraciones preparadas por PDO son suficientes para evitar la inyección de SQL? por ircmaxell. Además, cito una parte de su respuesta:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));

1160
2017-10-12 13:28



Primero, comencemos con el comentario estándar que damos a todos:

Por favor, no uses mysql_* funciones en nuevo código. Ya no se mantienen y están oficialmente desaprobados. Ver el caja roja? Aprender acerca declaraciones preparadas en su lugar, y uso DOP o MySQLi - Este artículo te ayudará a decidir cuál. Si elige PDO, aquí hay un buen tutorial.

Repasemos esto, oración por oración, y expliquemos:

  • Ya no se mantienen, y están oficialmente desaprobados.

    Esto significa que la comunidad PHP está retirando gradualmente el soporte para estas funciones muy antiguas. ¡Es probable que no existan en una versión futura (reciente) de PHP! El uso continuo de estas funciones puede romper su código en el futuro (no tan) lejano.

    ¡NUEVO! - ext / mysql es ahora oficialmente desaprobado a partir de PHP 5.5!

    ¡Más nuevo! ext / mysql ha sido eliminado en PHP 7.

  • En cambio, deberías aprender sobre declaraciones preparadas

    mysql_* la extensión no es compatible declaraciones preparadas, que es (entre otras cosas) una contramedida muy efectiva contra Inyección SQL. Se corrigió una vulnerabilidad muy grave en las aplicaciones dependientes de MySQL que permite a los atacantes obtener acceso a su secuencia de comandos y realizar cualquier consulta posible en tu base de datos

    Para más información, ver ¿Cómo puedo prevenir la inyección SQL en PHP?

  • Ver el cuadro rojo?

    Cuando vas a cualquier mysql página del manual de funciones, verá un recuadro rojo, explicando que ya no se debe usar.

  • Use ya sea PDO o MySQLi

    Hay alternativas mejores, más robustas y bien construidas, PDO - Objeto de base de datos PHP, que ofrece un enfoque OOP completo para la interacción con la base de datos, y MySQLi, que es una mejora específica de MySQL.


279
2017-12-24 23:30



Facilidad de uso

Las razones analíticas y sintéticas ya fueron mencionadas. Para los recién llegados hay un incentivo más significativo para dejar de usar las funciones mysql_ fechadas.

Las API de bases de datos contemporáneas son más fácil usar.

Es principalmente el parámetros vinculados que puede simplificar el código. Y con excelentes tutoriales (como se ve arriba) la transición a DOP no es demasiado arduo

Reescribir una base de código más grande a la vez, sin embargo, lleva tiempo. Raison d'être para esta alternativa intermedia:

Funciones pdo_ * equivalentes en lugar de mysql_ *

Utilizando <pdo_mysql.php> puedes cambiar de las antiguas funciones mysql_ con esfuerzo mínimo. Agrega pdo_ envolturas de funciones que reemplazan sus mysql_ contrapartes.

  1. Simplemente include_once("pdo_mysql.php"); en cada script de invocación que tiene que interactuar con la base de datos.

  2. Eliminar el mysql_ prefijo de función en todos lados y reemplazarlo con pdo_.

    • mysql_connect() se convierte pdo_connect()
    • mysql_query() se convierte pdo_query()
    • mysql_num_rows() se convierte pdo_num_rows()
    • mysql_insert_id() se convierte pdo_insert_id()
    • mysql_fetch_array() se convierte pdo_fetch_array()
    • mysql_fetch_assoc() se convierte pdo_fetch_assoc()
    • mysql_real_escape_string() se convierte pdo_real_escape_string()
    • y así... 

       
  3. Su código funcionará de la misma manera y seguirá teniendo el mismo aspecto:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

Et voilà.
Tu código es utilizando PDO.
Ahora es el momento de realmente utilizar eso.

Los parámetros enlazados pueden ser fáciles de usar

Solo necesitas una API menos difícil de manejar.

pdo_query() agrega soporte muy fácil para los parámetros enlazados. La conversión de código antiguo es sencilla:

Mueva sus variables fuera de la cadena SQL.

  • Añádalos como parámetros de función delimitados por comas a pdo_query().
  • Colocar signos de interrogación ? como marcadores de posición donde las variables estaban antes.
  • Deshacerse de ' comillas simples que anteriormente incluían valores de cadena / variables.

La ventaja se vuelve más obvia para un código más largo.

A menudo, las variables de cadena no solo se interpolan en SQL, sino que se concatenan con llamadas de escape intermedias.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

Con ? los marcadores de posición aplicados no tienen que preocuparse por eso:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Recuerde que pdo_ * todavía permite Cualquiera o.
Simplemente no escapar de una variable y enlazarlo en la misma consulta.

  • La función de marcador de posición la proporciona el PDO real detrás de ella.
  • Así también permitido :named listas de marcadores de posición más tarde.

Lo que es más importante, puede pasar $ _REQUEST [] variables de forma segura detrás de cualquier consulta. Cuando se envía <form> los campos coinciden exactamente con la estructura de la base de datos:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

Tanta simplicidad Pero volvamos a algunos consejos de reescritura y razones técnicas sobre por qué es posible que desee deshacerse de mysql_y escapando

Arreglar o eliminar cualquier oldschool sanitize() función

Una vez que hayas convertido todo mysql_ llamadas a pdo_query con params enlazados, elimine todos los redundantes pdo_real_escape_string llamadas.

En particular, deberías arreglar cualquier sanitize o clean o filterThis o clean_data funciones según lo anunciado por tutoriales anticuados de una forma u otra:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

La falla más evidente aquí es la falta de documentación. Más significativamente, el orden de filtrado estaba exactamente en el orden incorrecto.

  • El orden correcto habría sido: despreciablemente stripslashes como la llamada más profunda, entonces trimdespués strip_tags, htmlentities para el contexto de salida, y por último, el _escape_string ya que su aplicación debería preceder directamente al intersparsing de SQL.

  • Pero como primer paso solo deshacerse de _real_escape_string llamada.

  • Puede que tenga que guardar el resto de su sanitize() función por ahora si su base de datos y flujo de aplicaciones esperan cadenas de seguridad de contexto HTML. Agregue un comentario de que aplica solo escapes de HTML a partir de ahora.

  • El manejo de cadena / valor se delega a PDO y sus sentencias parametrizadas.

  • Si hubo alguna mención de stripslashes() en su función de desinfección, puede indicar una supervisión de mayor nivel.

    Nota histórica sobre magic_quotes. Esa característica está desaprobada. A menudo se describe incorrectamente como fallido seguridad característica sin embargo. Pero las magic_quotes son tanto una característica de seguridad fallida como las pelotas de tenis han fallado como fuente de nutrición. Ese simplemente no era su propósito.

    La implementación original en PHP2 / FI lo introdujo explícitamente con solo "las comillas se escapan automáticamente, lo que facilita la transferencia de datos de formulario directamente a consultas msql". Cabe destacar que fue seguro usarlo por accidente con mSQL, ya que eso solo admite ASCII.
      Luego PHP3 / Zend reintrodujo magic_quotes para MySQL y lo documentó incorrectamente. Pero originalmente era solo una característica de conveniencia, no pretendemos seguridad.

Cómo se diferencian las declaraciones preparadas

Cuando codifica variables de cadena en las consultas SQL, no solo se vuelve más complejo para seguir. También es un esfuerzo extraño para MySQL separar el código y los datos de nuevo.

Las inyecciones SQL simplemente son cuando los datos sangran en el código contexto. Un servidor de base de datos no puede detectar más tarde dónde PHP pegó originalmente las variables entre las cláusulas de consulta.

Con los parámetros enlazados, separa el código SQL y los valores del contexto SQL en su código PHP. Pero no vuelve a mezclarse detrás de escena (excepto con PDO :: EMULATE_PREPARES). Su base de datos recibe los comandos SQL no variados y los valores de las variables 1: 1.

Si bien esta respuesta hace hincapié en que debe preocuparse por las ventajas de legibilidad de caer mysql_. Ocasionalmente también hay una ventaja de rendimiento (INSERT repetidos con solo valores diferentes) debido a esta separación visible y técnica de datos / códigos.

Tenga en cuenta que el enlace de parámetros todavía no es una solución mágica de una sola parada contra todas Inyecciones de SQL. Maneja el uso más común para datos / valores. Pero no puedo incluir en la lista blanca el nombre de la columna / los identificadores de la tabla, la ayuda con la construcción dinámica de la cláusula o simplemente las listas de valores de la matriz.

Uso híbrido de PDO

Estas pdo_* Las funciones de envoltura crean una API de paso intermedio amigable con la codificación. (Es más o menos lo que MYSQLI podría haber sido si no fuera por el cambio de firma de la función idiosincrásica). También exponen el PDO real la mayoría de las veces.
La reescritura no tiene que detenerse en el uso de los nuevos nombres de funciones pdo_. Podría hacer una transición de uno a uno cada pdo_query () en una llamada simple $ pdo-> prepare () -> execute ().

Sin embargo, es mejor comenzar a simplificar nuevamente. Por ejemplo, la obtención de resultados comunes:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

Puede ser reemplazado con solo una iteración foreach:

foreach ($result as $row) {

O mejor aún una recuperación de matriz directa y completa:

$result->fetchAll();

Obtendrá advertencias más útiles en la mayoría de los casos que PDO o mysql_ generalmente brindan después de consultas fallidas.

Otras opciones

Así que esto espero visualizar algunos práctico razones y un camino digno de dejar caer mysql_.

Solo cambio a  no acaba de cortarlo. pdo_query() también es solo una interfaz en él.

A menos que también introduzca el enlace de parámetros o pueda utilizar algo más de la API más agradable, es un cambio inútil. Espero que se represente lo suficientemente simple como para no fomentar el desánimo de los recién llegados. (La educación generalmente funciona mejor que la prohibición).

Si bien califica para la categoría de cosa más simple que posiblemente podría funcionar, también es un código muy experimental. Lo acabo de escribir durante el fin de semana. Sin embargo, hay una plétora de alternativas. Solo google para Abstracción de base de datos PHP y navega un poco. Siempre ha habido y habrá muchas bibliotecas excelentes para tales tareas.

Si desea simplificar aún más la interacción de su base de datos, a los mapeadores les gusta Paris / Idiorm vale la pena intentarlo. Al igual que nadie usa el DOM insulso en JavaScript, no es necesario que cuides a una interfaz de base de datos en bruto hoy en día.


200
2017-10-12 13:22



los mysql_ funciones:

  1. están desactualizados, ya no se mantienen
  2. no le permite moverse fácilmente a otro backend de base de datos
  3. no admiten declaraciones preparadas, por lo tanto,
  4. alentar a los programadores a utilizar la concatenación para generar consultas, lo que lleva a vulnerabilidades de inyección de SQL

134
2018-01-01 17:42



Hablando de técnico razones, solo hay unas pocas, extremadamente específicas y raramente usadas. Lo más probable es que nunca los uses en tu vida.
Tal vez soy demasiado ignorante, pero nunca tuve la oportunidad de usar cosas como

  • consultas asincrónicas no bloqueantes
  • procedimientos almacenados que devuelven múltiples resultados
  • Encriptación (SSL)
  • Compresión

Si los necesita, estos son sin duda razones técnicas para pasar de la extensión mysql a algo más elegante y moderno.

Sin embargo, también hay algunos problemas no técnicos, que pueden hacer que su experiencia sea un poco más difícil

  • el uso posterior de estas funciones con las versiones modernas de PHP aumentará los avisos de nivel obsoleto. Ellos simplemente pueden ser apagados.
  • en un futuro lejano, es posible que se eliminen de la versión predeterminada de PHP. Tampoco es un gran problema, ya que mydsql ext se trasladará a PECL y todos los proveedores de servicios estarán encantados de compilar PHP con él, ya que no quieren perder clientes cuyos sitios funcionaron durante décadas.
  • fuerte resistencia de la comunidad Stackoverflow. Cada vez que mencionas estas funciones honestas, te dicen que están bajo estricto tabú.
  • Al ser un usuario promedio de PHP, lo más probable es que su idea de usar estas funciones sea propensa a errores e incorrecta. Solo por todos estos numerosos tutoriales y manuales que te enseñan de la manera incorrecta. No las funciones en sí, tengo que enfatizarlas, sino la forma en que se usan.

Este último problema es un problema.
Pero, en mi opinión, la solución propuesta tampoco es mejor.
Me parece demasiado idealista un sueño que todos los usuarios de PHP aprenderán a manejar consultas SQL de una vez. Lo más probable es que simplemente cambien mysql_ * a mysqli_ * mecánicamente, dejando el enfoque del mismo. Especialmente porque mysqli hace que el uso de declaraciones preparadas sea increíblemente doloroso y problemático.
Sin mencionar eso nativo declaraciones preparadas no son suficientes para proteger desde inyecciones SQL, y ni mysqli ni PDO ofrecen una solución.

Entonces, en lugar de luchar contra esta extensión honesta, preferiría luchar contra prácticas incorrectas y educar a las personas de la manera correcta.

Además, hay algunas razones falsas o no significativas, como

  • No es compatible con los procedimientos almacenados (estábamos usando mysql_query("CALL my_proc"); por edades)
  • No admite transacciones (igual que el anterior)
  • No es compatible con múltiples declaraciones (¿quién las necesita?)
  • No en desarrollo activo (¿qué? ¿Afecta?  de alguna manera práctica?)
  • Carece de una interfaz OO (para crear una es cuestión de varias horas)
  • No admite declaraciones preparadas o consultas parametrizadas

El último es un punto interesante. Aunque mysql ext no es compatible nativo declaraciones preparadas, no son necesarias para la seguridad. Fácilmente podemos falsificar declaraciones preparadas usando marcadores de posición manejados manualmente (al igual que PDO):

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

voila, todo está parametrizado y es seguro.

Pero está bien, si no le gusta el cuadro rojo en el manual, surge un problema de elección: ¿mysqli o PDO?

Bueno, la respuesta sería la siguiente:

  • Si comprende la necesidad de usar un capa de abstracción de base de datos y buscando una API para crear una, mysqli es una muy buena opción, ya que de hecho es compatible con muchas características específicas de mysql.
  • Si, al igual que la gran mayoría de la gente de PHP, estás usando llamadas API sin procesar directamente en el código de la aplicación (que es esencialmente una práctica incorrecta) - PDO es la única opción, ya que esta extensión pretende ser no solo API sino más bien un semi-DAL, aún incompleta pero ofrece muchas características importantes, con dos de ellas hace que PDO se distinga de manera crítica de mysqli:

    • a diferencia de mysqli, PDO puede enlazar marcadores de posición por valor, lo que hace factibles consultas generadas dinámicamente sin varias pantallas de código bastante desordenado.
    • a diferencia de mysqli, PDO siempre puede devolver el resultado de la consulta en una matriz usual simple, mientras que mysqli puede hacerlo solo en instalaciones de mysqlnd.

Por lo tanto, si usted es un usuario promedio de PHP y desea ahorrarse un montón de dolores de cabeza al usar declaraciones nativas preparadas, PDO, nuevamente, es la única opción.
Sin embargo, PDO tampoco es una bala de plata y tiene sus dificultades.
Entonces, escribí soluciones para todos los escollos comunes y casos complejos en el PDO tag wiki

Sin embargo, todo el mundo hablando de extensiones siempre falta el 2 hechos importantes sobre Mysqli y PDO:

  1. Declaración preparada no es una bala de plata. Hay identificadores dinámicos que no pueden vincularse usando declaraciones preparadas. Hay consultas dinámicas con un número desconocido de parámetros que hace que la creación de consultas sea una tarea difícil.

  2. Ni mysqli_ * ni las funciones de PDO deberían haber aparecido en el código de la aplicación.
    Debería haber un capa de abstracción entre ellos y el código de la aplicación, que hará todo el trabajo sucio de encuadernación, bucles, manejo de errores, etc. en el interior, haciendo que el código de la aplicación sea SECO y limpio. Especialmente para casos complejos como la creación dinámica de consultas.

Entonces, cambiar a PDO o mysqli no es suficiente. Uno tiene que usar un ORM, o un generador de consultas, o cualquier clase de abstracción de base de datos en lugar de llamar a funciones API sin formato en su código.
Y al contrario: si tienes una capa de abstracción entre el código de tu aplicación y la API de mysql, en realidad no importa qué motor se usa. Puedes usar mysql ext hasta que quede obsoleto y luego reescribir fácilmente tu clase de abstracción a otro motor, teniendo todo el código de la aplicación intacto.

Aquí hay algunos ejemplos basados ​​en mi clase safemysql para mostrar cómo debe ser una clase de abstracción así:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Compare esta única línea con cantidad de código que necesitará con DOP.
Luego compara con gran cantidad de código necesitarás con declaraciones preparadas de Mysqli sin procesar. Tenga en cuenta que el manejo de errores, el perfilado y el registro de consultas ya están integrados y en ejecución.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Compárelo con las inserciones habituales de PDO, cuando cada nombre de campo se repite de seis a diez veces, en todos estos numerosos marcadores de posición, enlaces y definiciones de consulta.

Otro ejemplo:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Difícilmente puede encontrar un ejemplo para que PDO maneje ese caso práctico.
Y será demasiado prolijo y muy probablemente inseguro.

Por lo tanto, una vez más, no es solo un controlador en bruto, sino una clase de abstracción, útil no solo para los ejemplos tontos del manual para principiantes, sino para resolver cualquier problema real.


98
2017-10-12 13:23



Hay muchas razones, pero quizás la más importante es que esas funciones fomentan las prácticas de programación inseguras porque no son compatibles con las declaraciones preparadas. Las declaraciones preparadas ayudan a prevenir ataques de inyección SQL.

Cuando usas mysql_* funciones, debe recordar ejecutar los parámetros proporcionados por el usuario a través de mysql_real_escape_string(). Si olvida en un solo lugar o si escapa solo a una parte de la información, su base de datos puede estar sujeta a ataques.

Usando declaraciones preparadas en PDO o mysqli lo hará para que estos tipos de errores de programación sean más difíciles de hacer.


87
2017-10-12 13:24



Porque (entre otras razones) es mucho más difícil garantizar que los datos de entrada se desinfecten. Si utiliza consultas parametrizadas, como lo hace con PDO o mysqli, puede evitar el riesgo por completo.

Como ejemplo, alguien podría usar "enhzflep); drop table users" como nombre de usuario Las funciones anteriores permitirán la ejecución de múltiples declaraciones por consulta, por lo que algo así como ese desagradable cabrón puede eliminar una tabla completa.

Si uno fuera a usar PDO de mysqli, el nombre de usuario terminaría siendo "enhzflep); drop table users".

Ver bobby-tables.com.


70
2017-09-18 12:28