Back to Blog
Tutorial

SQL Injection in 2024: Yes, It's Still a Thing

Alex Thompson
December 16, 2024
5 min read

SQL injection has been around for decades, yet it remains one of the most common and dangerous vulnerabilities. Why haven't we solved this problem?

#SQL injection#web security#secure coding#OWASP#application security

SQL injection. It's been on the OWASP Top 10 since... forever. We've known how to prevent it for over 20 years. Yet here we are in 2024, and SQL injection is still causing massive breaches.

What's going on?

The Persistent Problem

SQL injection vulnerabilities occur when an application includes untrusted data in SQL queries without proper sanitization. Attackers can inject malicious SQL code, potentially:

  • Extracting entire databases
  • Modifying or deleting data
  • Bypassing authentication
  • Executing operating system commands (in some configurations)

We've had the solution forever: use parameterized queries (prepared statements). So why does this keep happening?

Why SQL Injection Persists

1. Legacy Code

Organizations have millions of lines of code written before secure coding was standard practice. That old PHP app from 2008? Probably vulnerable.

Rewriting legacy systems is expensive and risky. So they keep running, vulnerable SQL injection and all.

2. Framework Misuse

Modern frameworks make it easy to use parameterized queries. They also make it easy to bypass those protections when you need "dynamic" queries.

Example (dangerous):

const query = `SELECT * FROM users WHERE username = '${req.body.username}'`;
db.query(query);

The developer knew about SQL injection but thought "this input is validated on the frontend" or "I need dynamic query building here."

3. Complex Queries

Sometimes you need to dynamically build complex queries - filtering, sorting, pagination. Developers often resort to string concatenation because it's easier than properly parameterizing everything.

4. ORMs Aren't Perfect

Object-Relational Mappers (ORMs) handle most SQL safely, but they usually have "raw query" escape hatches for complex operations. Developers use those and reintroduce SQL injection.

5. New Developers

Every year, new developers enter the field. Not all of them learn secure coding practices from day one. They make the same mistakes we've been making for decades.

6. Time Pressure

"Ship it now, we'll fix security later." Spoiler: later rarely comes.

Modern SQL Injection Techniques

Attackers have evolved their techniques:

Time-Based Blind SQL Injection

Even when you don't see query results, attackers can exfiltrate data by measuring response times:

IF (SELECT COUNT(*) FROM users WHERE username='admin') > 0 
  WAITFOR DELAY '00:00:05'

If the response takes 5 seconds, the condition was true.

Second-Order SQL Injection

The malicious payload gets stored in the database, then executed later in a different part of the application:

  1. Attacker registers with username: admin'--
  2. Application safely stores this in the database
  3. Later, a different function retrieves this username and uses it in an unsafe query
  4. SQL injection executes in step 3, not step 1

NoSQL Injection

Plot twist: NoSQL databases can be vulnerable too!

MongoDB example:

db.users.find({ username: req.body.username });

If req.body.username is { $ne: null }, it matches ALL users.

ORM Injection

Even ORMs can be exploited if you're not careful:

User.where("name = '#{params[:name]}'")

That's Ruby on Rails with raw SQL conditions - vulnerable to injection.

Real-World Impact

SQL injection isn't theoretical. Major breaches include:

  • TalkTalk (2015): 157,000 customer records stolen via SQL injection
  • Fortnite (2019): Account takeover vulnerability using SQL injection
  • Freepik (2020): 8.3 million user records exposed
  • Thousands of unnamed SMBs: Many breaches never make headlines

The average cost of a data breach in 2024? Over $4 million.

Defense Strategies

The good news: SQL injection is very preventable.

1. Parameterized Queries (Always)

The gold standard:

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

The database engine treats parameters as data, never as executable code.

2. Stored Procedures (Done Right)

Stored procedures can help, but only if they use parameterization internally. Don't just move the vulnerable code into a stored procedure.

3. Input Validation

Validate input types and formats. If you're expecting a number, ensure it's actually a number.

BUT: Don't rely on input validation alone. Always use parameterized queries.

4. Principle of Least Privilege

Database accounts should have minimal necessary permissions. Your web app probably doesn't need DROP TABLE rights.

5. Web Application Firewalls (WAF)

WAFs can detect and block SQL injection attempts. They're not a substitute for proper coding, but they add defense in depth.

6. Static Analysis

Tools like SonarQube, Snyk Code, and Checkmarx can automatically detect SQL injection vulnerabilities in code.

7. Runtime Protection

Runtime application self-protection (RASP) tools monitor application behavior and can block SQL injection attempts.

Testing for SQL Injection

How do you know if you're vulnerable?

Manual Testing

Basic tests:

  • Add a single quote (') to input fields - do you get database errors?
  • Try ' OR 1=1-- - does behavior change?
  • Submit ' AND SLEEP(5)-- - does the response delay?

Automated Scanning

Tools like:

  • SQLmap (free, powerful)
  • Burp Suite (commercial)
  • OWASP ZAP (free)

Code Review

Review all database queries in your codebase. Look for:

  • String concatenation in queries
  • Raw SQL in ORMs
  • User input directly in queries

The Framework-Specific Guide

PHP/MySQL

$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);

Python/PostgreSQL

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

Node.js/MySQL

connection.query("SELECT * FROM users WHERE id = ?", [userId], callback);

Java/JDBC

PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
stmt.setInt(1, userId);

C#/.NET

var command = new SqlCommand("SELECT * FROM users WHERE id = @id", connection);
command.Parameters.AddWithValue("@id", userId);

Common Misconceptions

"I'm using an ORM, so I'm safe" ORMs help, but raw queries and unsafe method calls can still introduce vulnerabilities.

"I validate input on the frontend" Client-side validation can be bypassed. Always validate and sanitize server-side.

"I escape special characters" Manual escaping is error-prone. Use parameterized queries instead.

"My database user has limited permissions" Good! But attackers can still steal whatever data that user can access.

"We have a WAF" Defense in depth is good, but WAFs can be bypassed. Fix the code.

Making SQL Injection History

To actually eliminate SQL injection, we need:

1. Education: Teach secure coding from day one 2. Tooling: Make the secure way the easy way 3. Enforcement: Block unsafe code in CI/CD pipelines 4. Culture: Security is everyone's responsibility 5. Legacy Remediation: Dedicated time to fix old code

The Bottom Line

SQL injection in 2024 is inexcusable. We know how to prevent it. We have the tools. We have the frameworks.

What we need is discipline: always use parameterized queries, no exceptions. Review code for database interactions. Test regularly.

It's time to make SQL injection a thing of the past. One codebase at a time.

Views: 250

Back to Blog