PDO::ATTR_EMULATE_PREPARES
debt(d5/e1/b3/t7)
Closest to 'specialist tool catches it' (d5), because detection_hints.tools lists semgrep with a code_pattern checking for missing PDO::ATTR_EMULATE_PREPARES => false in the PDO constructor. This requires a dedicated semgrep rule rather than a default linter, placing it at d5 rather than d3.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: add PDO::ATTR_EMULATE_PREPARES => false in the PDO constructor options array and ensure charset=utf8mb4 in the DSN. Both changes land in the single PDO construction call, making this a one-line (or two-value) patch.
Closest to 'localised tax' (b3). The setting lives in the PDO connection configuration — typically one place in the codebase (a DB factory or config file). Once set correctly it imposes no ongoing tax on other files or maintainers, keeping it localised rather than cross-cutting.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field states that developers believe emulated prepares are just as safe as native ones, but with non-utf8mb4 charsets (e.g. GBK) the PHP-side escaping can be corrupted via multi-byte character injection. The default MySQL PDO driver ships with emulation ON, meaning the safe-looking default is actually the dangerous one — directly contradicting reasonable expectations about prepared statements providing SQL-injection safety.
Also Known As
TL;DR
Explanation
When EMULATE_PREPARES is true (the default in some drivers), PDO performs string interpolation in PHP before sending the final SQL — defeating the security benefit of prepared statements in certain edge cases involving non-standard character encodings. Setting it to false forces the database to handle parameterisation natively. Native prepared statements also provide marginal performance benefits when the same statement is executed multiple times.
How It's Exploited
Common Misconception
Why It Matters
Common Mistakes
- Leaving EMULATE_PREPARES at its default (true for MySQL driver) and trusting it is safe.
- Setting EMULATE_PREPARES false without also setting charset=utf8mb4 in the DSN.
- Assuming EMULATE_PREPARES=false breaks anything — it rarely does for standard CRUD operations.
Avoid When
- Do not leave EMULATE_PREPARES at its default (true) — it performs client-side escaping instead of structural parameterisation.
When To Use
- Always set EMULATE_PREPARES to false alongside charset=utf8mb4 in the DSN for maximum security.
Code Examples
// Emulated prepares + wrong charset = potential injection via multi-byte encoding
$pdo = new PDO('mysql:host=localhost;dbname=app', $user, $pass);
// PDO::ATTR_EMULATE_PREPARES defaults to true — PHP does the escaping, not MySQL
$pdo = new PDO(
'mysql:host=localhost;dbname=app;charset=utf8mb4', // charset in DSN
$_ENV['DB_USER'],
$_ENV['DB_PASS'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false, // native prepared statements
]
);