SQL Injection & NoSQL Injection
Why Injection Attacks Matter
Injection attacks have been the #1 threat on the OWASP Top 10 for over a decade โ and for good reason. A single unescaped user input can give an attacker full control over your database. They can read every user's password, delete entire tables, or even execute operating system commands on your server.
Modern APIs use multiple types of databases: PostgreSQL and MySQL (relational/SQL), MongoDB (NoSQL/document), and GraphQL APIs. Each has its own injection vectors, and each requires specific prevention techniques.
Why this matters for your career:
- SQL Injection is the most tested vulnerability in every penetration exam (OSCP, GPEN)
- Finding an SQLi in a bug bounty program typically pays $2,000 to $20,000+
- Understanding injection attacks is fundamental to writing secure backend code
- NoSQL injection is increasingly common with MongoDB's popularity
SQL vs NoSQL Injection: A Quick Comparison
| Feature | SQL Injection | NoSQL Injection |
|:--------|:-------------|:----------------|
| Target | Relational databases (PostgreSQL, MySQL, SQLite) | Document databases (MongoDB, CouchDB) |
| Attack vector | String concatenation in SQL queries | Operator injection ($ne, $gt, $where) |
| Exploit method | ' OR 1=1 --, UNION SELECT | {"$gt": ""}, {"$ne": ""} |
| Defense | Parameterized queries, ORM | Input validation, type checking, strict schemas |
| Prevalence | Declining (frameworks auto-protect) | Increasing (MongoDB popularity) |
๐ฅ Vibe Prompt
"Test a login API for SQLi. Show vulnerable vs parameterized query. Then test MongoDB NoSQLi."
import sqlite3, json
# VULNERABLE
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (id INT, username TEXT, password TEXT)")
conn.execute("INSERT INTO users VALUES (1, 'admin', 'secret123')")
def vulnerable_login(username, password):
# NEVER do this!
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
print(f"Query: {query}")
return conn.execute(query).fetchone() is not None
# SQLi attack
print(f"SQLi: {vulnerable_login("admin'--", "anything")}") # Bypasses auth!
# SAFE: parameterized
safe_query = "SELECT * FROM users WHERE username=? AND password=?"
print(f"Safe: {conn.execute(safe_query, ('admin'"'--", 'anything')).fetchone()}") # Fails properly
# NoSQL injection (MongoDB)
# Vulnerable: db.users.find({username: req.body.username, password: req.body.password})
# Attack: {"username": "admin", "password": {"$ne": ""}} โ matches!
# Safe fix:
# db.users.find({username: req.body.username, password: hash(req.body.password)})
Prevention
| Technique | How |
|-----------|-----|
| Parameterized Queries | WHERE id = %s |
| ORM | SQLAlchemy, Prisma |
| Input Validation | Reject special chars in usernames |
| Least Privilege | DB user = SELECT only |
| WAF | Block SQLi patterns |
Blind SQLi Detection
' OR 1=1 -- # Always true
' AND 1=2 -- # Always false
' AND SLEEP(5) -- # Time-based detection
Key Points
- Understand the core concepts thoroughly
- Practice with hands-on code examples
- Apply knowledge to real-world problems
- Review and reinforce through exercises
Further Learning
- Official documentation
- Open source projects on GitHub
- Community forums and discussions
- Related courses and tutorials
SQL Injection in APIs
SQL injection remains one of the most critical API vulnerabilities. Attackers inject SQL commands through input fields.
Detection
| Input | Expected Behavior | SQL Injection Test |
|-------|------------------|-------------------|
| 1 | Returns user 1 | 1 OR 1=1 โ returns all users |
| ' | Error | Tests for unescaped quotes |
| 1; DROP TABLE users-- | Error or table dropped | Tests for command chaining |
| 1 UNION SELECT * FROM passwords-- | Extra data in response | Tests for UNION injection |
Blind SQL Injection
When no visible output, attackers use boolean or time-based techniques.
# Boolean-based: check if condition is true
curl "https://api.example.com/users/1 AND 1=1" # Normal response
curl "https://api.example.com/users/1 AND 1=2" # Different response
# Time-based: sleep if condition is true
curl "https://api.example.com/users/1; IF (1=1) WAITFOR DELAY '0:0:5'--"
NoSQL Injection
NoSQL databases (MongoDB) are also vulnerable to injection attacks.
MongoDB Injection
// โ Vulnerable: direct interpolation
app.get('/api/users', async (req, res) => {
const { username } = req.query;
// If username = { "$ne": "" }, this returns ALL users!
const users = await db.collection('users').find({
username: username
}).toArray();
res.json(users);
});
// โ
Safe: validate input type
app.get('/api/users', async (req, res) => {
const { username } = req.query;
if (typeof username !== 'string') {
return res.status(400).json({ error: 'Invalid input type' });
}
const users = await db.collection('users').find({
username: { $eq: username }
}).toArray();
res.json(users);
});
NoSQL Injection Payloads
| Endpoint | Payload | Effect |
|----------|---------|--------|
| /api/users?username[$ne]= | {$ne: ""} | Returns all users |
| /api/login | {"password": {"$gt": ""}} | Bypasses password check |
| /api/search?q[$regex]=.* | {$regex: ".*"} | Returns all documents |
Prevention
| Database | Prevention Method |
|----------|------------------|
| SQL (PostgreSQL, MySQL) | Parameterized queries ($1, ? placeholders) |
| SQL (any) | ORM (Prisma, TypeORM, SQLAlchemy) |
| MongoDB | Use $eq operator, validate types |
| MongoDB | Use Mongoose schemas with strict validation |
Parameterized Query Example
# Python + psycopg2 โ parameterized
import psycopg2
conn = psycopg2.connect("dbname=test")
cur = conn.cursor()
# โ
Safe: %s placeholders
cur.execute(
"SELECT * FROM users WHERE username = %s AND password_hash = %s",
(username, password_hash)
)
# โ Vulnerable: string formatting
cur.execute(
f"SELECT * FROM users WHERE username = '{username}'"
)
Summary
SQL and NoSQL injection attacks exploit unsanitized input. Parameterized queries, ORMs, and input type validation are the standard defenses.
Key takeaways:
- SQL injection: inject SQL through input โ always use parameterized queries
- Blind injection: infer data through boolean or timing responses โ never ignore response timing variations
- NoSQL injection: MongoDB accepts operators (
$ne,$gt,$regex) โ always restrict to$eq - Detection: test with
',OR 1=1,UNION SELECTโ if the response changes, you have a vulnerability - Parameterized queries: use
%s,$1,?placeholders or an ORM โ never use string formatting - Input type validation: reject objects/arrays when expecting strings โ critical for MongoDB
- Mongoose/Prisma schemas: define expected types to prevent operator injection
What Is Next: Rate Limiting and JWT Session Security
Now that you understand how attackers inject malicious input into your database, the next chapter covers a different class of vulnerability โ rate limiting and session management. You will learn how attackers brute force passwords without restriction, how token theft works through XSS, and how to implement refresh token rotation for bulletproof session security.
Next Chapter: JWT and Session Attacks
The next chapter covers JWT attacks and session security.
Automated SQL Injection Testing
Using sqlmap for automated detection:
# Basic test
sqlmap -u "https://api.example.com/users?id=1"
# Test with POST data
sqlmap -u "https://api.example.com/login" --data="username=admin&password=test"
# Test headers
sqlmap -u "https://api.example.com/users" --headers="Authorization: Bearer *"
# Get database names
sqlmap -u "https://api.example.com/users?id=1" --dbs
# Get tables from a database
sqlmap -u "https://api.example.com/users?id=1" -D mydb --tables
You've completed this chapter! Next: JWT and session attacks.