php - assoc - Escapar nombres de columna en declaraciones PDO
php pdo get rows (5)
¿Qué tal algo como esto?
function filter_identifier($str, $extra='''') {
return preg_replace(''/[^a-zA-Z0-9_''.$extra.'']/'', '''', $str);
}
try {
$res = $db->query(''SELECT ''.filter_identifier($_GET[''column''], ''/*'').'' FROM ''.filter_identifier($_GET[''table'']).'' WHERE id = ?'', $id);
} catch (PDOException $e) {
die(''error querying database'');
}
Esta es una simple lista de personajes basada en listas blancas. Cualquier personaje que no esté en la lista será eliminado. Afortunadamente para mí, pude hacer la base de datos y las tablas, así que sé que nunca habrá caracteres fuera de "a-zA-Z0-9_" (nota: sin espacio). Puede agregar caracteres adicionales a la lista a través del argumento $ extra. Si alguien tratara de poner "(SELECCIONAR * FROM usuarios); -" en ''columna'', se filtraría hacia abajo a "SELECCIONAR * FROMusers" , lo que arrojaría una excepción :)
Intento evitar hacer consultas adicionales si es posible (soy muy sensible al rendimiento). Así que cosas como hacer un DESCRIBE de antemano o codificar una matriz de tablas / columnas para controlar es algo que preferiría no hacer.
Actualmente estoy creando una consulta en la que las partes de campo / columna y valor posiblemente consistan en datos ingresados por el usuario.
El problema es escapar de los nombres de campo. Estoy usando declaraciones preparadas para escapar y citar correctamente los valores, pero al escapar de los nombres de campo me encuentro con problemas.
- mysql_real_escape_string requiere un recurso de conexión mysql para nosotros, por lo que se descarta
- La cita de PDO :: agrega comillas alrededor de los nombres de campo que los hace inútiles en una consulta también
- Agrega pestañas funciona pero no es realmente seguro
Alguien tiene una idea sobre cuál es la mejor manera de insertar correctamente los nombres de campo en la consulta antes de pasarla a PDO :: prepare?
Esto puede afectar el rendimiento, pero debe ser seguro.
Primero ejecute una consulta de tabla DESCRIBE para obtener una lista de los nombres de campo permitidos, luego haga coincidir estos a pesar de los datos enviados por el usuario.
Si hay una coincidencia, puede usar los datos enviados por el usuario sin necesidad de escaparse.
Si no hay coincidencia, entonces se trata de un error tipográfico o de un truco; en cualquier caso, se trata de un "error" en los datos ingresados y la consulta no debe ejecutarse.
Lo mismo podría hacerse para los nombres de tabla ''dinámicos'' ejecutando una consulta SHOW TABLES y haciendo coincidir desde ese conjunto de resultados.
En una de mis aplicaciones tengo una secuencia de comandos de ''instalación''; Parte de esto consulta los nombres de campo de la base de datos y de la tabla y luego escribe un archivo php al que siempre se hace referencia para que no esté ejecutando constantemente DESCRIBE consultas sobre la base de datos, por ejemplo
$db_allowed_names[''tableName1''][''id''] = 1;
$db_allowed_names[''tableName1''][''field1''] = 1;
$db_allowed_names[''tableName1''][''field2''] = 1;
$db_allowed_names[''tableName2''][''id''] = 1;
$db_allowed_names[''tableName2''][''field1''] = 1;
$db_allowed_names[''tableName2''][''field2''] = 1;
$db_allowed_names[''tableName2''][''field3''] = 1;
if($db_allowed_names[''tableName1''][$_POST[''field'']]) {
//ok
}
Utilizo claves de matriz como esta ya que la instrucción if es un poco más rápida que una búsqueda en in_array
La forma estándar de ANSI de hacer un identificador delimitado es:
SELECT "field1" ...
y si hay un "en el nombre, doblarlo:
SELECT "some""thing" ...
Lamentablemente, esto no funciona en MySQL con la configuración predeterminada, porque MySQL prefiere pensar que las comillas dobles son una alternativa a las comillas simples para los literales de cadena. En este caso, debe utilizar los trazos inversos (como lo describe Björn) y la barra invertida de escape.
Para hacer que la barra invertida se escape correctamente, necesitaría mysql_real_escape_string, porque depende del conjunto de caracteres. Pero el punto es irrelevante, porque ni mysql_real_escape_string ni addslashes escapan del carácter de comillas inversas . Si puede estar seguro de que nunca habrá caracteres que no sean ASCII en los nombres de las columnas, puede salirse con la simple barra invertida, escapando de los caracteres `y /.
De cualquier manera, esto no es compatible con otras bases de datos. Puede decirle a MySQL que permita la sintaxis de ANSI al configurar la opción de configuración ANSI_QUOTES. Del mismo modo, SQL Server también ahoga las comillas dobles de forma predeterminada; usa otra sintaxis, a saber, corchetes. De nuevo, puede configurarlo para admitir la sintaxis ANSI con la opción ''identificador_citado''.
Resumen: si solo necesitas compatibilidad con MySQL:
a. use comillas inversas y no permita el carácter de comillas inversa, barra invertida y nul en los nombres porque escaparse de ellas es poco confiable
Si necesita compatibilidad cross-DBMS, ya sea:
segundo. utilice comillas dobles y requiera que los usuarios de MySQL / SQL-Server cambien la configuración apropiadamente. No permita caracteres de comillas dobles en el nombre (ya que Oracle no puede manejarlos, incluso escapó). O,
do. tener una configuración para MySQL vs SQL Server vs Others, y producir ya sea la contraqueración, el corchete o la sintaxis de doble cita, dependiendo de eso. No permitir las comillas dobles y la barra inclinada invertida / contracuenta / nul.
Esto es algo que esperaría que la capa de acceso a datos tuviera una función, pero PDO no.
Resumen del resumen: los nombres de columna arbitrarios son un problema, es mejor evitarlos si puede evitarlo.
Resumen del resumen del resumen: gnnnnnnnnnnnh.
La respuesta correcta es
str_replace("`", "``", $fieldname)
Incorrecto:
mysql> SELECT `col/"umn` FROM user; ERROR 1054 (42S22): Unknown column ''col/"umn'' in ''field list''
Derecha:
mysql> SELECT `kid``s` FROM user; ERROR 1054 (42S22): Unknown column ''kid`s'' in ''field list'' mysql> SELECT ```column``name``` FROM user; ERROR 1054 (42S22): Unknown column ''`column`name`'' in ''field list''
(Tenga en cuenta que en el último ejemplo, el nombre de la columna tiene 3 (tres) marcas atrás adicionales, solo para mostrar un caso extremo)
Un diseño extraño de un proyecto, pero para su problema: rodee sus nombres de campo con `y use addslashes para el nombre también.
select `field1`, `field2` from table where `field3`=:value