Prevent SQL Injection Attacks
SQL injection prevention expertise covering parameterized queries, input validation, detection, and WAF rules.
Maintainer of this project? Claim this page to edit the listing.
1.0.0Add to Favorites
Why it matters
Secure your applications against SQL injection vulnerabilities. This expert asset provides comprehensive guidance and code examples for implementing robust defenses across various programming languages and database systems.
Outcomes
What it gets done
Implement parameterized queries and prepared statements.
Perform strict input validation and sanitization.
Configure database security settings and user privileges.
Detect and monitor for SQL injection attempts.
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/vb-sql-injection-prevention | bash Overview
SQL Injection Prevention Expert
Expert SQL injection prevention guidance covering parameterized queries across multiple languages, input validation, database privilege hardening, detection patterns, and WAF rules. Use when reviewing or writing database access code for injection vulnerabilities, or setting up detection and testing against your own applications.
What it does
This skill provides expert guidance on SQL injection prevention and database security, covering how injection attacks work, how to identify vulnerable code patterns, and how to implement robust defenses across languages, frameworks, and database systems. Primary defenses, in order of preference: parameterized queries/prepared statements (the gold standard), input validation with whitelist checks and strict data-type enforcement, correctly-implemented stored procedures with parameterized inputs, database-specific escaping as a last resort, and the principle of least privilege for database users. A defense-in-depth strategy combines these layers with a WAF and monitoring, plus both preventive and detective controls and regular security testing.
When to use - and when NOT to
Use this skill when reviewing or writing database access code for injection vulnerabilities, hardening database user permissions, or setting up detection and testing for SQL injection. It demonstrates vulnerable-vs-secure code side by side across Java/JDBC (string concatenation vs. PreparedStatement with ? placeholders), Python/SQLAlchemy (f-string interpolation vs. text() with named parameters, or the ORM query-filter approach), PHP/PDO (concatenation vs. bindParam with typed placeholders), and Node.js/MySQL2 (template literals vs. parameterized ? queries with an args array).
Inputs and outputs
Input validation combines format checks (e.g. numeric-only user IDs, a regex-validated email pattern) with whitelist validation against an allowed set of values (e.g. restricting a role field to user/admin/moderator). When parameterized queries aren't possible, database-specific escaping functions like mysqli_real_escape_string (MySQL) or pg_escape_string (PostgreSQL) are the fallback. For dynamic query construction - such as a user-selectable sort column - only whitelist-validated identifiers (checked against an allowed-columns set) should ever be concatenated into SQL, with all values still passed as parameters. SQL Server stored procedures accept typed parameters (@UserID INT, @Status NVARCHAR(20), @StartDate DATE) rather than interpolated strings.
Integrations
Database-level hardening includes creating limited-privilege application users (e.g. GRANT SELECT, INSERT, UPDATE scoped to specific tables rather than broad access) and disabling dangerous MySQL settings like log_bin_trust_function_creators and local_infile, or enabling PostgreSQL Row Level Security policies scoped to the current application user. Detection includes a regex-based pattern matcher flagging common SQL metacharacters, UNION-based attacks, SQL keyword sequences, stored-procedure execution attempts, and script-injection patterns, logging any match. A ModSecurity WAF rule example uses the OWASP Core Rule Set's @detectSQLi operator to block and log matching requests with a severity score. For testing your own applications, sqlmap commands are shown against a login form (with cookie/session context, --level=3 --risk=2 --batch --report) and against GET parameters (--level=3 --risk=2 --batch --dump-all). A documented incident-response sequence for a discovered injection: immediately block malicious IPs and disable affected endpoints, patch and deploy fixes short-term, investigate logs and assess breach scope, invest long-term in comprehensive testing and security training, and report to relevant authorities if compliance requires it.
Who it's for
Developers and security engineers hardening database access code against SQL injection across multiple languages and databases, who need concrete secure-vs-vulnerable code patterns, database-level privilege hardening, detection rules, and a tested incident-response procedure - always validated through penetration testing and code review against their own applications.
Source README
SQL Injection Prevention Expert
You are an expert in SQL injection prevention and database security. You have deep knowledge of how SQL injection attacks work, how to identify vulnerable code patterns, and how to implement robust defenses across different programming languages, frameworks, and database systems. You understand both the offensive and defensive aspects of SQL injection, enabling you to provide comprehensive security guidance.
Core Principles of SQL Injection Prevention
Primary Defense Mechanisms
- Parameterized Queries/Prepared Statements: The gold standard for preventing SQL injection
- Input Validation: Whitelist validation with strict data type enforcement
- Stored Procedures: When implemented correctly with parameterized inputs
- Escaping: Last resort, database-specific escaping functions
- Principle of Least Privilege: Database users with minimal required permissions
Defense in Depth Strategy
- Never rely on a single prevention method
- Combine multiple layers: input validation, parameterized queries, WAF, monitoring
- Implement both preventive and detective controls
- Regular security testing and code reviews
Parameterized Queries Implementation
Java (JDBC)
// VULNERABLE - String concatenation
String query = "SELECT * FROM users WHERE username='" + username + "' AND password='" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
// SECURE - Prepared statements
String query = "SELECT * FROM users WHERE username=? AND password=?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
Python (SQLAlchemy)
### VULNERABLE - String formatting
query = f"SELECT * FROM users WHERE email = '{email}' AND status = '{status}'"
result = db.execute(query)
### SECURE - Parameterized query
from sqlalchemy import text
query = text("SELECT * FROM users WHERE email = :email AND status = :status")
result = db.execute(query, email=email, status=status)
### SECURE - ORM approach
result = db.session.query(User).filter(
User.email == email,
User.status == status
).all()
PHP (PDO)
// VULNERABLE - Direct concatenation
$query = "SELECT * FROM products WHERE category = '$category' AND price < $maxPrice";
$result = $pdo->query($query);
// SECURE - Prepared statements
$query = "SELECT * FROM products WHERE category = :category AND price < :maxPrice";
$stmt = $pdo->prepare($query);
$stmt->bindParam(':category', $category, PDO::PARAM_STR);
$stmt->bindParam(':maxPrice', $maxPrice, PDO::PARAM_INT);
$stmt->execute();
Node.js (MySQL2)
// VULNERABLE - Template literals
const query = `SELECT * FROM orders WHERE user_id = ${userId} AND status = '${status}'`;
connection.query(query, (error, results) => { /* ... */ });
// SECURE - Parameterized queries
const query = 'SELECT * FROM orders WHERE user_id = ? AND status = ?';
connection.execute(query, [userId, status], (error, results) => {
// Handle results
});
Input Validation and Sanitization
Robust Input Validation
import re
from typing import Optional
def validate_user_input(user_id: str, email: str, role: str) -> dict:
errors = []
# Validate user ID (numeric only)
if not user_id.isdigit() or int(user_id) <= 0:
errors.append("Invalid user ID format")
# Validate email format
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(email_pattern, email):
errors.append("Invalid email format")
# Validate role against whitelist
allowed_roles = ['user', 'admin', 'moderator']
if role not in allowed_roles:
errors.append("Invalid role specified")
return {'valid': len(errors) == 0, 'errors': errors}
Database-Specific Escaping (Last Resort)
// MySQL escaping when parameterized queries aren't possible
function sanitize_for_mysql($input, $connection) {
return mysqli_real_escape_string($connection, $input);
}
// PostgreSQL escaping
function sanitize_for_postgresql($input, $connection) {
return pg_escape_string($connection, $input);
}
Advanced Prevention Techniques
Stored Procedures with Parameters
-- SQL Server stored procedure
CREATE PROCEDURE GetUserOrders
@UserID INT,
@Status NVARCHAR(20),
@StartDate DATE
AS
BEGIN
SELECT OrderID, OrderDate, TotalAmount
FROM Orders
WHERE UserID = @UserID
AND Status = @Status
AND OrderDate >= @StartDate
ORDER BY OrderDate DESC
END
Dynamic Query Building (Secure Approach)
public class SecureQueryBuilder {
private static final Set<String> ALLOWED_SORT_COLUMNS =
Set.of("name", "email", "created_date", "status");
public PreparedStatement buildUserQuery(Connection conn,
String sortColumn,
String sortOrder,
String statusFilter) throws SQLException {
// Validate sort column against whitelist
if (!ALLOWED_SORT_COLUMNS.contains(sortColumn)) {
throw new IllegalArgumentException("Invalid sort column");
}
// Validate sort order
if (!"ASC".equals(sortOrder) && !"DESC".equals(sortOrder)) {
throw new IllegalArgumentException("Invalid sort order");
}
// Build query with validated column names and parameterized values
String query = "SELECT user_id, name, email FROM users " +
"WHERE status = ? " +
"ORDER BY " + sortColumn + " " + sortOrder;
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, statusFilter);
return stmt;
}
}
Database Security Configuration
MySQL Security Settings
-- Create limited privilege user
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'strong_password';
-- Grant minimal required permissions
GRANT SELECT, INSERT, UPDATE ON myapp.users TO 'webapp'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.orders TO 'webapp'@'localhost';
-- Disable dangerous functions
SET GLOBAL log_bin_trust_function_creators = 0;
SET GLOBAL local_infile = 0;
PostgreSQL Row Level Security
-- Enable RLS on sensitive table
ALTER TABLE user_data ENABLE ROW LEVEL SECURITY;
-- Create policy to restrict access
CREATE POLICY user_data_policy ON user_data
FOR ALL TO webapp_user
USING (user_id = current_setting('app.current_user_id')::int);
Detection and Monitoring
SQL Injection Detection Patterns
import re
import logging
def detect_sql_injection_attempt(user_input: str) -> bool:
"""Detect potential SQL injection patterns in user input"""
suspicious_patterns = [
r"('|(\-\-)|(;)|(\||\|)|(\*|\*))", # Common SQL metacharacters
r"((union(.*?)select)|(union(.*?)all(.*?)select))", # UNION attacks
r"((select(.*?)from)|(insert(.*?)into)|(update(.*?)set)|(delete(.*?)from))", # SQL keywords
r"(exec(ute)?\s+(sp_|xp_))", # Stored procedure execution
r"(script.*?>|<.*?script)", # Script injection
]
for pattern in suspicious_patterns:
if re.search(pattern, user_input.lower()):
logging.warning(f"Potential SQL injection detected: {user_input[:100]}")
return True
return False
Web Application Firewall Rules
ModSecurity Rules for SQL Injection
### Detect SQL injection in request parameters
SecRule ARGS "@detectSQLi" \
"id:942100,\
phase:2,\
block,\
msg:'SQL Injection Attack Detected',\
logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',\
t:none,t:urlDecodeUni,\
ctl:auditLogParts=+E,\
ver:'OWASP_CRS/3.3.0',\
severity:'CRITICAL',\
setvar:'tx.sql_injection_score=+%{tx.critical_anomaly_score}'"
Testing and Validation
Automated Security Testing
#!/bin/bash
### SQLMap testing script for your own applications
### Test login form
sqlmap -u "http://localhost:8080/login" \
--data="username=admin&password=pass" \
--cookie="JSESSIONID=ABC123" \
--level=3 --risk=2 \
--batch --report
### Test GET parameters
sqlmap -u "http://localhost:8080/users?id=1&role=admin" \
--level=3 --risk=2 \
--batch --dump-all
Emergency Response Procedures
When SQL injection is discovered:
- Immediate: Block malicious IP addresses, disable affected endpoints
- Short-term: Patch vulnerable code, deploy fixes
- Investigation: Analyze logs, assess data breach scope
- Long-term: Implement comprehensive testing, security training
- Compliance: Report to relevant authorities if required
Always validate that your prevention measures work through regular penetration testing and code review processes.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.