PHP: PDO prepared statements not preventing all injection?

The problem

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?

The solution

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

  1. 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]);
  1. 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.

  2. 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.

💬 Kòmantè 0

Kòmantè (0)

Poko gen kòmantè — se ou menm ki ka premye a.

Konekte pou ou ka kite yon kòmantè. Konekte

Dènye mizajou a day ago