SQL Injection Prevention Techniques
This page covers the primary defense strategies recommended by OWASP and security experts.
1. Parameterized Queries (Prepared Statements)
The most effective defense against SQL injection.
Parameterized queries ensure user input is treated as data, never as executable SQL code.
How They Work
- The SQL statement structure is defined with placeholders
- User input is bound to placeholders separately
- The database treats input as literal values, not SQL code
Examples by Language
Java (JDBC):
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, username);
stmt.setString(2, password);
ResultSet results = stmt.executeQuery();
Python (SQLite/psycopg2):
# SQLite uses ? placeholders
cursor.execute(
"SELECT * FROM users WHERE username = ? AND password = ?",
(username, password)
)
# PostgreSQL uses %s placeholders
cursor.execute(
"SELECT * FROM users WHERE username = %s AND password = %s",
(username, password)
)
PHP (PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
// With named parameters
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute(['username' => $username]);
Rust (with Oxide SQL):
See the builder module rustdoc for parameterized query examples.
2. Stored Procedures
Stored procedures can provide protection when implemented correctly.
Safe Implementation
CREATE PROCEDURE GetUserByUsername
@Username NVARCHAR(50)
AS
BEGIN
SELECT * FROM Users WHERE Username = @Username
END
Unsafe Implementation (Still Vulnerable!)
-- DON'T DO THIS - still vulnerable to injection
CREATE PROCEDURE GetUserByUsername
@Username NVARCHAR(50)
AS
BEGIN
EXEC('SELECT * FROM Users WHERE Username = ''' + @Username + '''')
END
Best Practices
- Use
sp_executesqlinstead ofEXECfor dynamic SQL in SQL Server - Never concatenate parameters into SQL strings
- Validate input within the procedure
- Apply least privilege to procedure execution
3. Input Validation
Input validation should be a secondary defense, not the primary one.
Allowlist Validation
import re
def validate_username(username):
# Accept only alphanumeric characters and underscores
if not re.match(r'^[a-zA-Z0-9_]{3,20}$', username):
raise ValueError("Invalid username format")
return username
def validate_id(user_id):
# Force to integer
return int(user_id)
Type Enforcement
function sanitizeInput(input) {
// Force to string and remove dangerous characters
return String(input).replace(/['"\\;]/g, '');
}
Input validation alone is NOT sufficient. Always use parameterized queries as the primary defense.
4. Least Privilege Principle
Configure database accounts with minimal necessary permissions.
-- Create a read-only user for web application
CREATE USER 'webapp_readonly'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT ON mydb.products TO 'webapp_readonly'@'localhost';
GRANT SELECT ON mydb.categories TO 'webapp_readonly'@'localhost';
-- Create a user with limited write access
CREATE USER 'webapp_writer'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT, INSERT, UPDATE ON mydb.orders TO 'webapp_writer'@'localhost';
-- No DELETE, DROP, or ALTER privileges
Key Principles
- Never use admin accounts (
sa,root) for web applications - Separate read and write accounts where possible
- Restrict access to specific tables needed
- Never grant
FILE,PROCESS, orSUPERprivileges
5. Web Application Firewalls (WAFs)
WAFs provide a defense-in-depth layer by blocking known attack patterns.
How WAFs Help
- Pattern matching against SQL keywords
- Anomaly detection for unusual requests
- Signature database for known exploits
Limitations
WAFs should NOT be your only defense:
- Known bypass techniques exist
- False positives can block legitimate traffic
- New attack patterns may not be recognized
6. Error Handling
Never expose database errors to users.
try:
cursor.execute(query, params)
except DatabaseError as e:
# Log the detailed error internally
logger.error(f"Database error: {e}")
# Return generic message to user
return "An error occurred. Please try again later."
OWASP Recommended Priority
- Parameterized Queries - Primary defense
- Stored Procedures - When implemented safely
- Allowlist Input Validation - For untrusted input
- Escaping User Input - Last resort