PHP: PDO prepared statements not preventing all injection?
posée 10 days ago156 vuesen
I use PDO prepared statements everywhere but a security review flagged a query where the table name is dynamic. How do I safely handle dynamic identifiers?
🤖 Diagnostic IA
Généré par IA. Ce n'est pas une réponse — la communauté ci-dessous le confirme ou le corrige. Vérifiez toujours avant de vous y fier.
3 réponses
Short answer
Prepared statement placeholders (? or :name) only work for values (data), not for identifiers like table or column names. PDO has no built-in way to bind an identifier — that's why your dynamic table name query isn't actually protected, even though you're "using prepared statements."
What to do instead
- Whitelist validation — the safest and most common approach. Only allow table names that exist in a known, hardcoded list:
$allowedTables = ['users', 'orders', 'invoices'];
if (!in_array($tableName, $allowedTables, true)) {
throw new InvalidArgumentException('Invalid table name');
}
$sql = "SELECT * FROM `$tableName` WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute(['id' => $id]);
-
If you truly can't whitelist, at minimum strictly validate the format (e.g. only allow
[A-Za-z0-9_]+matching an existing schema table) and quote the identifier properly for your DB engine (backticks for MySQL, double quotes for Postgres). This is weaker than a whitelist and still risky if validation is incomplete — whitelist is strongly preferred. -
Never concatenate user input directly into the identifier position and never try to "escape" it the same way you'd escape a value — identifiers aren't values, so
PDO::quote()is not designed for this and shouldn't be relied on for table/column names.
Bottom line
Values → use prepared statement placeholders. Identifiers (table/column names) → validate against a whitelist before building the SQL string.
Connectez-vous pour dire si ça a marché.
Server Action for form mutations tied to your own DB and revalidation; Route Handler when you need a stable HTTP endpoint (webhooks, external/mobile clients, non-React consumers). Both must re-validate auth server-side.
Connectez-vous pour dire si ça a marché.
Prepared statements only parameterize values, not identifiers. For a dynamic table name, whitelist against a known set:
$allowed = ['orders', 'invoices'];
if (!in_array($table, $allowed, true)) throw new InvalidArgumentException();Connectez-vous pour dire si ça a marché.
Sign in and verify your email to post an answer.