FlawOpen

flawopen.com/SQL Injection/Is escaping enough?

Is escaping quotes enough to prevent SQL injection?

CWE-89: Improper Neutralization of Special Elements used in an SQL Command (SQL Injection)Reference page
Short answer

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

Connection character sets can change escaping behavior

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.

One missed call anywhere breaks the whole defense

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.

Escaping targets quotes — not every injection vector needs one

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.

VULNERABLE
// User input is interpolated into SQL syntax
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
HARDENED
// 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

FAQ

Is escaping ever the right tool?

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.

If the driver escape function is maintained, is it safe?

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

See also SQL Injection (CWE-89): How It Works and How to Prevent It in Every Language — flawopen.com Is Using an ORM Enough to Prevent SQL Injection? Is Knex.js Safe from SQL Injection? MyBatis ${} vs #{}: Prevent SQL Injection MySQLi vs PDO: Secure Prepared Statements Is NHibernate Safe from SQL Injection? MongoDB NoSQL Injection: Safe Query Filters