flawopen.com/SQL Injection in PHP/mysqli vs PDO
MySQLi vs PDO: which is safer against SQL injection?
A prepared statement keeps SQL instructions fixed and sends input values separately, like filling labeled blanks on a form. PDO and MySQLi can both do this. The safety comes from binding values correctly—not the library name.
The same query, prepared with either API
$userId = $_GET['id']; $sql = "SELECT id, email FROM users WHERE id = " . $userId; // Both calls execute the already-built SQL string: $rows = $mysqli->query($sql); $rows = $pdo->query($sql);
// PDO $stmt = $pdo->prepare( "SELECT id, email FROM users WHERE id = :id" ); $stmt->execute(["id" => $userId]); // MySQLi $stmt = $mysqli->prepare( "SELECT id, email FROM users WHERE id = ?" ); $stmt->bind_param("i", $userId); $stmt->execute();
Where the APIs differ
PDO exposes a common interface across database drivers; MySQLi targets MySQL and MariaDB. PDO does not make SQL portable by itself: SQL dialects, driver behavior, and supported features still differ.
PDO can use named placeholders such as :id or positional ? placeholders, subject to driver rules; do not mix styles in one statement. MySQLi uses positional ? placeholders. Both bind values, and neither can bind table names, column names, keywords, or sort directions.
PDO drivers may use native or emulated prepares. For MySQL, PDO::ATTR_EMULATE_PREPARES controls emulation when supported; drivers may emulate when native prepares are unavailable or fall back if a query cannot be prepared natively. Follow the driver’s documented behavior. This setting does not make interpolated SQL safe—keep query text fixed and bind data either way.
Rules that prevent SQL injection in either library
Use prepare() and bind every untrusted value. Keep SQL syntax fixed; choose dynamic identifiers from a strict server-side map or build a finite set of query variants. A prepared statement does not authorize access to a row, so scope queries to the authenticated user where required. For LIKE, binding prevents SQL syntax injection but % and _ remain wildcard characters unless escaped for literal matching.
Choosing between PDO and MySQLi
- ✓Choose PDO when you need a shared API across database drivers; verify SQL dialect and driver behavior during migration.
- ✓Choose MySQLi for a MySQL/MariaDB-only application that needs its MySQL-specific API.
- ✓For either API, review every query path for interpolation, concatenation, and dynamic identifiers; bind values and authorize records separately.
FAQ
No. Both can safely bind values when the query is prepared and values are bound. Named placeholders may improve readability; they do not bind SQL identifiers or change authorization requirements.
Not as a blanket rule. PDO driver support and fallback behavior vary. Check the driver documentation and application requirements. Regardless of mode, keep SQL structure fixed and pass values as parameters.
No. Bind parameters represent values, not SQL identifiers or keywords. Map a client choice to a finite set of trusted SQL fragments on the server.
References
- PHP Manual — PDO Prepared Statements
- PHP Manual — MySQLi Prepared Statements
- PHP Manual — PDO::setAttribute and Emulated Prepares
- FlawOpen — SQL Injection in PHP