SQL injection attacks exploit a simple design flaw: the application treats user-supplied text as part of the SQL instruction set rather than as data. Once you understand this, the fix becomes obvious — and the reason that naive string sanitisation does not fully solve the problem becomes clear too.

The Core Problem: Strings Are Instructions

When an application builds a SQL query by concatenating user input, it hands the database a string and asks it to interpret that string as a command. The database has no way to distinguish between the parts the developer intended and the parts the user inserted.

Consider a login check where the email and password fields are concatenated directly into the query. If a user submits anything' OR '1'='1 as the password, the resulting SQL condition evaluates as always true. The query returns all rows, and the application logs in as the first user returned — typically the admin account. No brute force, no password cracking. Just string manipulation.

Why Escaping Characters Is Not Enough

The naive fix is to escape or remove dangerous characters — single quotes, semicolons, comment sequences (--). This has two fundamental problems.

Encoding bypass: A single quote can be submitted in multiple encoded forms: %27 (URL encoding), ' (HTML entity), or Unicode variants. Depending on how the application decodes inputs, a blacklist that catches one form may miss another.

Context dependency: A character that is dangerous in one SQL context is harmless in another. Escaping based on a global blacklist will either over-escape (breaking legitimate inputs) or under-escape (missing attack vectors in numeric fields, which do not need quote characters at all).

Sanitisation is a defensive layer against known attack patterns. Parameterized queries prevent the attack by design, regardless of what the user submits.

How Parameterized Queries Work

A parameterized query sends the SQL structure and the data values to the database separately. The SQL structure is sent first and compiled into a query plan. The data values are sent after — bound to placeholders — and are never interpreted as SQL syntax.

The database receives the statement with ? placeholders, compiles it, and creates an execution plan. When the application provides the actual values, those values fill the placeholders as literal data. The database does not re-parse them as SQL. No matter what characters the user submits, they are treated as data. The malicious input that would have broken a concatenated query simply fails to match any stored password — as it should.

Named vs Positional Parameters

Different database drivers use different placeholder syntax. Positional parameters use ? and are filled in order. Named parameters use :name syntax and are matched by name, which is more readable in complex queries with many parameters. Named parameters also prevent errors where adding or removing a parameter from the middle of a query shifts the binding positions of all subsequent ones.

Stored Procedures Are Not a Magic Fix

Stored procedures are often cited as a defence against SQL injection. They can be, but only if they use parameterized queries internally. A stored procedure that concatenates its input parameters into a dynamic SQL string and executes it is just as vulnerable as inline concatenated SQL. The vulnerability is in the concatenation, not in where the SQL lives.

Every modern database library supports parameterized queries — PHP PDO, Python sqlite3, Java JDBC, Laravel query builder. Writing raw SQL with concatenation when a parameterized alternative is available is never justified. Use the SQL Formatter to inspect and clean SQL during development.