Injection Attacks (SQL & NoSQL)
Understanding Injection Attacks: Methods, Mindsets, and Prevention Tips

Cybersecurity student with a strong interest in Offensive Security. Passionate about ethical hacking, hands-on learning, and turning curiosity into practical skills that build stronger defenses.
A practical, defence-first guide to SQL and NoSQL injection. Learn what each flaw is, how attackers approach them conceptually, and robust, practical defenses, detection methods, and safe testing practices—without sharing exploit payloads.
Introduction
Injection vulnerabilities remain among the most damaging mistakes in web applications. At their core, injection flaws happen when user-controllable data is interpreted as code or control for a backend component (database, command shell, LDAP, etc.). This article focuses on SQL and NoSQL injection: what they are, common insecure patterns, safe code examples to fix them, detection strategies, and secure best practices.
⚠️ Reminder: This post intentionally avoids providing exploit techniques or attack payloads. If you want hands-on practice, use authorized labs (OWASP Juice Shop, WebGoat, PortSwigger Academy, DVWA) and never test live systems without permission.
1 — What is an Injection Attack (high level)
An injection occurs when an application builds commands, queries, or queries-like structures by combining untrusted input with program logic — allowing an attacker’s input to alter the intended behavior. For databases this can mean unauthorized data access, data modification, or denial of service.
Why it matters: Injection can expose sensitive data, corrupt databases, bypass access control, or enable a foothold into systems. The root cause is unsafe handling of untrusted input — not a single language or framework.
2 — Types we cover here
SQL Injection: Relational database queries (MySQL, PostgreSQL, MSSQL, etc.).
NoSQL Injection: Queries against document or key-value stores (MongoDB, CouchDB, etc.) where user input influences query objects or operators.
3 — Attacker Mindset (conceptual)
Attackers look for places where input flows into query builders, string concatenations, or APIs that accept expressions. They reason about where input crosses the boundary between data and code and try to make it change the control flow or returned results. This post describes those conceptual patterns — not how to exploit them.
4 — Common Unsafe Patterns (do NOT use in production)
Below are vulnerable coding patterns shown only for awareness (so you can recognize them) and their secure replacements. I avoid any exploit payloads.4.1 Unsafe SQL pattern: string concatenation (vulnerable pattern)
Problem: Building SQL by concatenating user input lets input change the query structure.
Vulnerable pattern (do not deploy):
# Python (vulnerable pattern)
def get_user_by_username(db_conn, username):
query = "SELECT id, username, email FROM users WHERE username = '" + username + "';"
return db_conn.execute(query).fetchall()
Why it's bad: username is placed directly into the SQL text. Any special characters or control sequences in username will change the query text.
Secure fix — parameterized query (correct):
# Python with parameterized queries (psycopg2 / DB-API style)
def get_user_by_username(db_conn, username):
query = "SELECT id, username, email FROM users WHERE username = %s;"
cursor = db_conn.cursor()
cursor.execute(query, (username,))
return cursor.fetchall()
Principle: Use parameter binding / prepared statements. Never build SQL by concatenating raw input.
4.2 Unsafe Node.js example (vulnerable pattern)
// Node.js (vulnerable pattern with string building)
app.get('/search', async (req, res) => {
let term = req.query.q || '';
let sql = "SELECT * FROM products WHERE name LIKE '%" + term + "%'";
const rows = await db.query(sql);
res.json(rows);
});
Secure fix — parameterized query (pg example):
// Node.js (secure)
app.get('/search', async (req, res) => {
const term = '%' + (req.query.q || '') + '%';
const sql = "SELECT * FROM products WHERE name LIKE $1";
const { rows } = await db.query(sql, [term]);
res.json(rows);
});
4.3 Unsafe PHP example (vulnerable pattern)
// PHP vulnerable (do not use)
$username = $_GET['u'];
$sql = "SELECT * FROM users WHERE username = '$username'";
$result = $pdo->query($sql);
Secure fix — PDO prepared statement:
// PHP secure (PDO)
$username = $_GET['u'] ?? '';
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username');
$stmt->execute(['username' => $username]);
$rows = $stmt->fetchAll();
4.4 NoSQL (MongoDB) — unsafe construction of query objects
NoSQL systems accept structured query objects. Danger arises when you accept raw JSON or merge user data into query objects without validation.
Vulnerable pattern (do not use):
// Node.js / Express (vulnerable)
app.post('/find', async (req, res) => {
// userQuery comes directly from request body and is used as-is
const userQuery = req.body.query;
const docs = await db.collection('items').find(userQuery).toArray();
res.json(docs);
});
Why it's risky: An attacker could include operators or data types in userQuery the app did not intend, altering query semantics.
Secure patterns (safe alternatives):
- Whitelisting expected fields / operators — only accept a controlled set of fields and build the DB query yourself from validated inputs.
// Node.js (secure example)
app.post('/find', async (req, res) => {
const filters = {};
if (typeof req.body.name === 'string') filters.name = { $regex: req.body.name, $options: 'i' };
if (typeof req.body.category === 'string') filters.category = req.body.category;
// ...only accepted fields allowed
const docs = await db.collection('items').find(filters).toArray();
res.json(docs);
});
- Use typed parameter APIs and avoid parsing raw JSON into query objects.
5 — Defensive Rules & Best Practices (detailed)
Always use parameterized queries / prepared statements. This is the single strongest defense for SQL injection.
For NoSQL, never trust user-supplied query objects. Build queries from validated inputs; whitelist allowed fields and operators.
Use ORMs or query builders responsibly — verify how they bind parameters and avoid raw query execution unless strictly necessary.
Principle of least privilege: database user used by the application should have only necessary permissions (e.g., no DROP or admin rights).
Input validation & canonicalization: validate and canonicalize incoming data, but treat validation as a complement — not a substitute — for parameterization.
Output encoding for contexts outside DB (e.g., rendering in HTML) — prevents other injection types (XSS).
Avoid dynamic queries when possible: prefer static queries with bound parameters.
Secure configuration: disable features that allow server-side execution of arbitrary expressions unless required.
Use safe database drivers and keep dependencies updated.
Implement query timeouts and limits to mitigate amplification or expensive queries.
6 — Detecting & Monitoring Injection Attempts
Application logs: log failed queries, query syntax errors, and unusual patterns (e.g., queries with uncommonly long inputs).
Database logs / slow query logs: watch for unexpected query shapes or frequent parsing errors.
WAF / Runtime protections: web application firewalls can block common malicious patterns but do not replace secure coding.
Anomaly detection: sudden spikes in expensive queries, unusual parameter shapes, or high error rates on DB endpoints.
Alerting: set alerts for repeated parameterized-query failures or abnormal access patterns.
7 — Testing Safely (authorized only)
Static analysis & SAST: run linters and security scanners that detect string concatenation or unsafe APIs.
Dependency scanning: identify risky libraries and known CVEs.
Unit & integration tests: include tests that assert parameterized execution and that certain inputs do not cause syntax errors.
Dynamic testing in lab: use purpose-built training apps (OWASP Juice Shop, WebGoat, DVWA, PortSwigger Academy) to learn how flaws manifest — only in isolated/test environments.
Pentests: perform authorized penetration tests; scope should be clear and written permission must exist.
8 — Example Secure Checklist (Quick)
All DB queries use parameter binding / prepared statements.
No raw user input is concatenated into query strings.
No parsing of raw user-provided JSON into DB queries without validation.
DB user privileges follow least privilege.
ORMs used correctly; raw queries minimized and audited.
Inputs validated and normalized server-side.
WAF, query timeouts, and monitoring in place.
Security tests in CI and authorized dynamic testing in sandboxed labs.
9 — Learning & Safe Practice Resources
If you want to practice or teach injection safely, use these authorized resources:
OWASP Juice Shop — intentionally vulnerable app for learning.
OWASP WebGoat — guided lessons on common vulnerabilities.
PortSwigger Academy — interactive lessons and labs.
Damn Vulnerable Web Application (DVWA) — controlled testing ground.
Official database docs — best practices for prepared statements and drivers.
Focus on safe coding, not tricks
Injection is easy to prevent when you follow established patterns: treat user input as data, use parameterized APIs, minimize privileges, and validate/whitelist inputs for NoSQL systems. Design your code so that even if user input is malicious, it cannot change the program’s control flow or database commands.


