SQL Injection Defense Best Practices: Protecting Database Security

SQL injection is one of the most classic and dangerous web vulnerabilities, consistently in OWASP Top 10 since 1998. The core principle: attackers embed malicious SQL code in user input fields.

Core Defense Strategies

1. Parameterized Queries (Prepared Statements)

Most effective defense. Separates SQL structure from data.

PHP (PDO):

$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username');
$stmt->execute(['username' => $username]);

Python (psycopg2):

cursor.execute("SELECT * FROM users WHERE username = %s", (username,))

2. ORM Frameworks

Modern ORMs use parameterized queries at the framework level.

Language ORM Safe Query
PHP Laravel Eloquent User::where('email', $email)->first()
Python Django ORM User.objects.get(email=email)
Python SQLAlchemy session.query(User).filter(User.email == email).first()
Node.js Prisma prisma.user.findUnique({ where: { email } })
Go GORM db.Where("email = ?", email).First(&user)

3. Input Validation & Whitelist

For dynamic identifiers (table names, column names, sort directions):

$allowedColumns = ['price', 'name', 'created_at'];
$sortBy = in_array($_GET['sort_by'], $allowedColumns) ? $_GET['sort_by'] : 'created_at';

Multi-Layer Defense

Layer Measure Goal
L1 Code Parameterized queries / ORM Block injection reaching DB
L2 Input WAF (Cloudflare, ModSecurity) Intercept before app
L3 Database Least privilege Limit impact if injection succeeds
L4 Monitoring SQL audit logs + anomaly detection Detect ongoing attacks

Common Mistakes

  • "Escaping input is enough" → Escaping can be bypassed with different charsets
  • "ORM is 100% safe" → ORM raw queries still have injection risk
  • "Only GET params need protection" → POST, Cookie, Headers are also vectors
  • "Internal systems don't need protection" → Insider attacks happen

Summary

Never trust user input. Use parameterized queries (or ORM) as primary defense, combine with WAF, least privilege DB permissions, and CI/CD security scanning.