¿Puedo usar una declaración preparada de PDO para vincular un identificador (una tabla o un nombre de campo) o una palabra clave de sintaxis?
Desafortunadamente, la declaración preparada solo puede representar un literal de datos. Entonces, una trampa muy común es una consulta como esta:
$opt = "id";
$sql = "SELECT :option FROM t WHERE id=?";
$stm = $pdo->prepare($sql);
$stm->execute(array($opt));
$data = $stm->fetchAll();
Depende de la configuración de PDO, esta consulta dará como resultado un error (en caso de usar declaraciones preparadas reales) o simplemente una cadena literal 'id'
en el campo (en caso de preparaciones emuladas).
Por lo tanto, un desarrollador debe encargarse de los identificadores por sí mismo:PDO no ofrece ayuda para este asunto.
Para que un identificador dinámico sea seguro, se deben seguir 2 reglas estrictas:
- para formatear correctamente el identificador
- para verificarlo con una lista blanca codificada .
Para formatear un identificador, se deben aplicar estas 2 reglas:
- Incluya el identificador en acentos graves.
- Escapa de los acentos graves del interior duplicándolos.
Después de dicho formato, es seguro insertar la variable $tabla en la consulta. Entonces, el código sería:
$field = "`".str_replace("`","``",$field)."`";
$sql = "SELECT * FROM t ORDER BY $field";
Sin embargo, aunque dicho formato sería suficiente para casos como ORDER BY, para la mayoría de los demás casos existe la posibilidad de un tipo diferente de inyección:permitir que un usuario elija una tabla o un campo que puede ver, podemos revelar algunos información confidencial, como contraseña u otros datos personales. Por lo tanto, siempre es mejor comparar los identificadores dinámicos con una lista de valores permitidos. He aquí un breve ejemplo:
$allowed = array("name","price","qty");
$key = array_search($_GET['field'], $allowed);
$field = $allowed[$key];
$query = "SELECT $field FROM t"; //value is safe
Para las palabras clave, las reglas son las mismas, pero, por supuesto, no hay formato disponible; por lo tanto, solo es posible y debe usarse la lista blanca:
$dir = $_GET['dir'] == 'DESC' ? 'DESC' : 'ASC';
$sql = "SELECT * FROM t ORDER BY field $dir"; //value is safe
Consulte también esta nota aportada por el usuario en la documentación de PHP:Nota del usuario sobre PDO::quote