¿Por qué no puedo pasar el nombre de la tabla a una declaración PDO preparada?
$stmt = $dbh->prepare('SELECT * FROM :table WHERE 1');
if ($stmt->execute(array(':table' => 'users'))) {
var_dump($stmt->fetchAll());
}
¿Hay otra forma segura de insertar un nombre de tabla en una consulta SQL? Con seguro, quiero decir que no quiero hacer
$sql = "SELECT * FROM $table WHERE 1"
array('u'=>'users', 't'=>'table', 'n'=>'nonsensitive_data')
etc.)default
. Si usa este patrón, debe etiquetar uno de suscase
default
default: throw new InvalidArgumentException;
if ( in_array( $tbl, ['users','products',...] ) { $sql = "SELECT * FROM $tbl"; }
. Gracias por la ideamysql_real_escape_string()
. Tal vez aquí puedo decirlo sin que alguien salte y diga "Pero no lo necesitas con DOP"Para entender por qué el enlace de un nombre de tabla (o columna) no funciona, debe comprender cómo funcionan los marcadores de posición en las declaraciones preparadas: no se sustituyen simplemente como cadenas (escapó adecuadamente) y se ejecuta el SQL resultante. En cambio, un DBMS al que se le pidió que "preparara" una declaración presenta un plan de consulta completo sobre cómo ejecutaría esa consulta, incluidas las tablas e índices que usaría, que serán las mismas independientemente de cómo complete los marcadores de posición.
El plan
SELECT name FROM my_table WHERE id = :value
será el mismo para lo que sea que sustituya:value
, pero lo aparentemente similarSELECT name FROM :table WHERE id = :value
no se puede planificar, porque el DBMS no tiene idea de qué tabla realmente seleccionará.Esto no es algo que una biblioteca de abstracción como PDO pueda o deba evitar, ya que anularía los 2 propósitos clave de las declaraciones preparadas: 1) permitir que la base de datos decida de antemano cómo se ejecutará una consulta y usar la misma planificar varias veces; y 2) para evitar problemas de seguridad separando la lógica de la consulta de la entrada variable.
fuente
TOP
/LIMIT
/OFFSET
, por lo que esto sería un poco fuera de lugar como característica.Veo que esta es una publicación antigua, pero la encontré útil y pensé en compartir una solución similar a lo que sugirió @kzqai:
Tengo una función que recibe dos parámetros como ...
En el interior, compruebo las matrices que configuré para asegurarme de que solo las tablas y columnas con tablas "bendecidas" sean accesibles:
Entonces la comprobación de PHP antes de ejecutar PDO se ve como ...
fuente
$pdo->query($sql)
Usar el primero no es inherentemente más seguro que el segundo, debe desinfectar la entrada, ya sea parte de una matriz de parámetros o una variable simple. Por lo tanto, no veo nada de malo en usar este último formulario
$table
, siempre y cuando se asegure de que el contenido$table
es seguro (¿alfanum más guiones bajos?) Antes de usarlo.fuente
(Respuesta tardía, consulte mi nota al margen).
La misma regla se aplica cuando se intenta crear una "base de datos".
No puede usar una declaración preparada para vincular una base de datos.
Es decir:
no trabajará. Use una lista segura en su lugar.
Nota al margen: agregué esta respuesta (como wiki de la comunidad) porque a menudo solía cerrar preguntas, donde algunas personas publicaban preguntas similares a esta al tratar de vincular una base de datos y no una tabla y / o columna.
fuente
Parte de mí se pregunta si podría proporcionar su propia función de desinfección personalizada tan simple como esta:
Realmente no lo he pensado, pero parece que eliminar todo, excepto los caracteres y los guiones bajos, podría funcionar.
fuente
MyLongTableName
es fácil de leer correctamente, pero si verifica el nombre almacenado sería (probablemente) elMYLONGTABLENAME
que no es muy legible, porMY_LONG_TABLE_NAME
lo que en realidad es más legible.Select * From $table
. Una lista blanca o una coincidencia de patrón estricta (por ejemplo, "nombres que comienzan el informe_ seguidos de 1 a 3 dígitos solamente") realmente es esencial aquí.En cuanto a la pregunta principal en este hilo, las otras publicaciones dejaron en claro por qué no podemos vincular valores a los nombres de columna al preparar declaraciones, por lo que aquí hay una solución:
Lo anterior es solo un ejemplo, por lo que no hace falta decir que copiar-> pegar no funcionará. Ajústate a tus necesidades. Ahora, esto puede no proporcionar un 100% de seguridad, pero permite cierto control sobre los nombres de columna cuando "entran" como cadenas dinámicas y pueden modificarse en los usuarios finales. Además, no es necesario crear una matriz con los nombres y tipos de columnas de la tabla, ya que se extraen del esquema de información.
fuente