SQL Injection and Database Security
SQL injection occurs when untrusted input becomes executable SQL syntax instead of remaining a bound data value.
SQL injection occurs when untrusted input becomes part of an SQL command as executable syntax instead of remaining a bound data value. The weakness is not limited to web forms; desktop clients, mobile applications, reporting tools, command-line programs and background jobs can introduce the same flaw.
SQL injection is the result of allowing untrusted input to alter the structure of a database command. The same failure mode appears across web, desktop and mobile data-access layers, so the defensive model is consistent: parameterised queries, least privilege, controlled error handling and sufficient observability to detect abnormal query behaviour.
Condition for Injection
The fundamental error is constructing SQL syntax and user input in the same string:
string sql = "SELECT id FROM users WHERE username='" + username + "' AND password='" + password + "'";An input containing quotes, comments or operators can then change the structure of the resulting query. Blocking selected characters is not a durable defense. Encoding differences, alternative syntax, database-specific behavior and second-order injection can bypass such filters.
Parameterized Queries
The SQL command and its values should be transmitted separately:
const string sql = "SELECT id FROM users WHERE username = @username AND password_hash = @passwordHash";
using SqlCommand command = new SqlCommand(sql, connection);
command.Parameters.Add("@username", SqlDbType.NVarChar, 128).Value = username;
command.Parameters.Add("@passwordHash", SqlDbType.VarBinary, 32).Value = passwordHash;A parameter protects a value. A table name, column name, sort direction or SQL keyword cannot normally be bound as a value parameter. Dynamic structural elements should therefore be selected from a fixed allowlist:
string orderBy = sortKey switch
{
"date" => "published_at",
"title" => "title",
_ => "id"
};Stored Procedures and ORM Boundaries
Using a stored procedure does not by itself prevent injection. If the procedure concatenates user-controlled input into dynamic SQL, the vulnerability remains. An ORM or query builder can likewise introduce injection through raw SQL, string interpolation or incorrectly constructed dynamic filters.
The security property is not the framework being used; it is the structural separation of commands from data. Code review should therefore cover every SQL-generation path, raw-query call, migration tool and reporting query under the same threat model.
Password Verification
Passwords should not be queried directly or stored in a reversible form. Password-derivation functions such as Argon2id, scrypt, or bcrypt/PBKDF2 with appropriate cost parameters should be used, with a unique salt for each password. The application should first retrieve the user record through a parameterized query and then verify the password using an appropriate library implementation.
A fast SHA-256 digest alone is not a password-storage scheme. Fast hash functions make offline password guessing inexpensive.
Least Privilege
An application account should have access only to the schemas and operations it actually requires. A read-only service should not receive privileges to create tables, manage users or write across unrelated schemas. Separate accounts can be used for different trust boundaries.
Least privilege limits the impact of an injection vulnerability, but it does not replace prevention. Sensitive data can still be read or modified within whatever privileges the compromised account legitimately has.
Second-Order Injection and Stored Input
An input value may be stored safely at first and only become dangerous later. If an administration tool, report or migration process subsequently concatenates that stored value into dynamic SQL, second-order injection occurs. Data read back from a database must therefore not be treated as trusted merely because it has already been stored.
Validation and Output Encoding
Input validation does not replace parameterized queries; it protects business rules. An identifier expected to be a positive integer, a language code limited to a known set, or a page size restricted to a defined range should be validated accordingly.
When data is rendered into HTML, context-appropriate output encoding is required. SQL parameters do not prevent XSS, and HTML encoding does not prevent SQL injection. Each trust boundary requires controls appropriate to its own syntax and execution context.
Error Messages and Logging
Clients should not receive SQL text, table names, driver stack traces or connection details. Server logs may contain diagnostic error classes and correlation identifiers, but should not record passwords, tokens, personal data or complete sensitive query parameters.
Detection should not rely on searching for a single suspicious character. Unusual error rates, rejected validations, unexpected query volumes and abnormal data-access patterns should be observed together.
Testing Approach
A security review should cover at least:
- Static analysis and code review
- Verification that values are bound as parameters
- Allowlist tests for dynamic identifiers
- Review of dynamic SQL inside stored procedures
- Database privilege-matrix testing
- Error-message and log-leakage checks
- Second-order injection scenarios
The central defense against SQL injection is to keep untrusted data structurally separate from SQL syntax. Parameterized queries, least privilege, explicit business-rule validation and controlled dynamic SQL significantly reduce the attack surface when applied together.
Hardening the Authorization Boundary Inside the Database
Parameterized SQL is fundamental to preventing SQL injection, but it does not by itself define the privilege boundary inside the database. In Oracle environments, Oracle Database Vault is a separate defense layer that can constrain what even highly privileged database accounts may access.
References
- **[1]** William G. J. Halfond; Jeremy Viegas; Alessandro Orso. (2006). A Classification of SQL-Injection Attacks and Countermeasures. Proceedings of the IEEE International Symposium on Secure Software Engineering.
- **[2]** Stephen W. Boyd; Angelos D. Keromytis. (2004). SQLrand: Preventing SQL Injection Attacks. Applied Cryptography and Network Security, Springer. doi:10.1007/978-3-540-24852-1_21
- **[3]** Sruthi Bandhakavi; Prithvi Bisht; P. Madhusudan; V. N. Venkatakrishnan. (2007). CANDID: Preventing SQL Injection Attacks Using Dynamic Candidate Evaluations. Proceedings of the 14th ACM Conference on Computer and Communications Security. doi:10.1145/1315245.1315249