← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

PDO::ATTR_EMULATE_PREPARES

PHP CWE-89 OWASP A3:2021 PHP 5.1+ Intermediate
debt(d5/e1/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

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.

e1 Effort Remediation debt — work required to fix once spotted

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.

b3 Burden Structural debt — long-term weight of choosing wrong

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.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

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.

About DEBT scoring →

Also Known As

emulated prepared statements PDO emulate prepares native prepared statements

TL;DR

Controls whether PDO sends real prepared statements to the database or emulates them client-side in PHP.

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

With emulated prepares enabled and a non-utf8mb4 charset, multi-byte character injection (GBK encoding attack) can bypass PHP's escaping and inject raw SQL into the query.

Common Misconception

Emulated prepares are just as safe as native ones. When charset is not utf8mb4 and emulation is on, some encodings allow injection through multi-byte characters that corrupt the escaping.

Why It Matters

Emulated prepares can be vulnerable to SQL injection via charset-based attacks (e.g. GBK encoding) because PHP performs the escaping, not the database. Disabling emulation closes this gap permanently.

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

✗ Vulnerable
// 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
✓ Fixed
$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
    ]
);

Added 31 Mar 2026
Views 42
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 2 pings S 2 pings S 2 pings M 0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 2 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Perplexity 6 Scrapy 5 Unknown AI 4 Google 3 ChatGPT 3 Ahrefs 3 SEMrush 2 Claude 1 Meta AI 1 Majestic 1 Bing 1 PetalBot 1
crawler 29 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Set PDO::ATTR_EMULATE_PREPARES => false in the PDO constructor options array alongside charset=utf8mb4 in the DSN
📦 Applies To
PHP 5.1+ web cli
🔗 Prerequisites
🔍 Detection Hints
new PDO(...) without PDO::ATTR_EMULATE_PREPARES => false
Auto-detectable: ✓ Yes semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line
CWE-89


✓ schema.org compliant