Injection
3) Injection
What it is (very simple)
Injection happens when user input is treated as instructions, not just data.
That is the core bug:
- App expects data.
- App builds a command/query with that data unsafely.
- Database/interpreter executes attacker-controlled instructions.
Mental model (important)
Think of two boxes:
- Data box: normal user values (name, email, search text).
- Code box: SQL commands, OS commands, template logic, etc.
Injection happens when user data “jumps” from the data box into the code box.
Common injection families
- SQL Injection: attacker changes database query behavior.
- NoSQL Injection: attacker manipulates NoSQL query/filter objects.
- OS Command Injection: attacker makes server run system commands.
- LDAP/XPath Injection: attacker alters directory/query logic.
- Template Injection: attacker injects template expressions that execute logic.
- XSS is also an injection style in the browser context (input becomes executable script).
Step-by-step example (SQL Injection)
Imagine login code builds SQL with string concatenation.
SELECT * FROM users WHERE email = '<input_email>' AND password = '<input_password>';
Attack flow:
- User enters crafted text instead of normal email/password.
- App directly inserts that text into SQL string.
- Final SQL now has altered logic.
- Database executes it as valid SQL.
- Auth check can be bypassed or extra data can be exposed.
Main lesson:
- The database is doing exactly what it was told.
- The app told it the wrong thing because it mixed code and untrusted data.
Why teams still get this wrong
- Fast coding with string concatenation.
- False confidence in frontend validation.
- Trying to “sanitize everything” with ad-hoc regex.
- Dynamic query building without strict allowlists.
- Legacy code and copy-paste query helpers.
Tricky concepts explained clearly
1) Prepared statements (most important defense)
Prepared statements separate query structure from values.
Bad idea:
- Build full SQL text by concatenating user input.
Good idea:
- Send SQL template with placeholders.
- Send user values separately as parameters.
- DB treats values only as data, never as SQL instructions.
Example:
// Bad const q = "SELECT * FROM users WHERE email = '" + email + "'";
// Good const q = "SELECT * FROM users WHERE email = ?"; db.query(q, [email]);
2) Escaping vs parameterization
Escaping means manually adding backslashes/quotes rules. Parameterization means DB driver handles values safely.
Practical rule:
- Prefer parameterized queries always.
- Do not rely on custom escaping as your main protection.
3) “But we validate input”
Validation helps, but it is not enough alone.
Why:
- Validation can miss edge cases.
- Different encodings can bypass weak filters.
- Business needs may require special characters anyway.
Correct logic:
- Validate for business rules.
- Parameterize for injection safety.
- Do both.
4) Blind injection
Sometimes app shows no SQL errors, so teams think they are safe.
Not necessarily true:
- Attackers can infer truth/false from response timing, status, or behavior.
- “No error message” is not proof of safety.
5) Second-order injection
Very tricky and often missed.
Flow:
- Attacker submits malicious input.
- App stores it in DB safely or unsafely.
- Later, another feature reads that stored value and builds a new query/command unsafely.
- Injection triggers later, not at initial input point.
So you must secure every execution point, not only the first input point.
Prevention strategy (logical order)
- Use parameterized queries everywhere for SQL/NoSQL access.
- Avoid dynamic query parts when possible.
- If dynamic parts are required (like sort column), map user options to a strict allowlist.
- Never pass raw user input to shell commands.
- Prefer safe system APIs over shell execution.
- Enforce server-side validation with allowlists for expected formats.
- Use least-privilege DB accounts.
- Hide detailed DB/system errors from users.
- Add SAST/DAST and secure code review checks for concatenated queries.
- Keep frameworks and DB drivers updated.
“Bad vs good” patterns
- Bad: "SELECT ... WHERE id=" + userInput
Good: "SELECT ... WHERE id=?" with parameter binding.
- Bad: exec("convert " + filename)
Good: use library API or safe execFile with strict argument list.
- Bad: taking sortBy directly from request and appending to SQL
Good: sortBy must match a fixed map: name, created_at, price.
Detection checklist for juniors
- Search codebase for string-built queries and command execution.
- Check every endpoint that accepts filters/search/sort/pagination.
- Test unexpected characters in inputs (', ", ;, template markers).
- Verify query APIs use placeholders/params.
- Review logs for DB syntax errors tied to user input.
- Add unit/integration tests for malicious input cases.
Business impact
- Account takeover.
- Data breach.
- Data tampering/deletion.
- Ransom/extortion risk.
- Compliance penalties and downtime.