flawopen.com/SQL Injection/Is escaping enough?
Is escaping quotes enough to prevent SQL injection?
Usually no. A quote-escaping function is tied to a database, connection character set, SQL mode, and exact quoting context. Parameterized queries keep values out of SQL syntax through the driver API. Use parameterization for values; for dynamic identifiers that cannot be bound, choose from a strict application-owned allow-list.
Why escaping is fragile in practice
Escaping is not a context-free string replacement. For example, MySQL documents that mysql_real_escape_string() uses the connection character set, and that issuing SET NAMES does not update the client library state used by that function. A character-set mismatch can therefore invalidate assumptions about escaped text. Prefer parameterized APIs; if legacy code must escape, use the driver API with the connection configured through its supported charset function and handle errors.
Escaping must be applied correctly at every point where a value is interpolated, with the correct driver, character set, SQL mode, and string-literal context. Parameterized APIs make the value/data boundary explicit at the query call and avoid manually assembling a quoted value.
Quote escaping only addresses characters inside a particular quoted string literal. It does not safely construct every SQL expression, such as a column name, table name, keyword, or sort direction, and it does not address input placed in an unquoted numeric expression. Bind data values; map any dynamic SQL structure to fixed application-owned choices.
// User input is interpolated into SQL syntax
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
// The driver binds name as a data value
cursor.execute(
"SELECT id FROM users WHERE name = ?",
(name,),
)
What parameter binding does differently
A parameterized query passes SQL structure and values through the database API as separate inputs. Drivers may use native prepared statements or another safe implementation, so avoid assuming a specific wire-protocol sequence. The security property is that the value is treated as data rather than reparsed as SQL syntax. In Python DB-API, for example, use the driver’s placeholder style and pass values separately; never build the query with string formatting.
Engineering checklist
- ✓Bind every user-controlled data value through the database driver API; never format it into SQL text.
- ✓Map dynamic table names, column names, and sort directions to fixed application-owned allow-lists.
- ✓If legacy escaping cannot yet be removed, use the database driver’s context-appropriate escaping API and verify connection character set and SQL mode.
- ✓Test query construction with quotes, non-ASCII input, and invalid identifiers without sending destructive statements.
FAQ
For an identifier or SQL fragment that cannot be bound, use only a driver-supported quoting API with a fixed, application-owned allow-list. Do not use general-purpose escaping on arbitrary values.
A maintained driver function can help in its documented context, but it remains sensitive to database, connection settings, quoting context, and correct use at every call site. Parameterized queries are the safer default for values.
References
- OWASP SQL Injection Prevention Cheat Sheet
- CWE-89 — SQL Injection
- PHP manual — mysqli::real_escape_string
- MySQL C API — mysql_real_escape_string
- flawopen.com — SQL Injection (all languages)