Technical Presentation
OWASP Top 10 — A03: Injection
The Complete Zero-to-Hero Guide
Where We Are in the Series
What Is Injection?
Simple definition
Injection happens when an application sends untrusted input to an interpreter as part of a command or query, and the interpreter treats that input as code instead of just data.
That is the heart of injection:
The system cannot properly separate data from instructions.
Real-life analogy
Imagine you go to a restaurant.
The waiter asks:
“What would you like to order?”
A normal customer says:
- “Chicken soup”
- “Pasta”
- “Tea”
But an attacker writes this on the order slip:
- “Chicken soup, and also unlock the cash register and give me all the money.”
If the kitchen and cashier blindly follow everything on the slip as instructions, the customer just changed the system’s behavior.
That is injection.
Visualizing the core problem
What this diagram means
In a safe system, user input remains just input. In a vulnerable system, attacker input crosses a line and becomes part of the command itself.
That is why injection is so dangerous: the attacker is no longer just “using” your application — they are changing what your application tells another component to do.
The Key Concept: Interpreters
Injection only makes sense if you understand interpreters.
An interpreter is something that reads instructions and executes them.
Common examples:
- SQL database engine
- operating system shell
- browser JavaScript engine
- LDAP engine
- XPath engine
- template engine
- NoSQL query parser
Important lesson
Injection is not “just SQL injection.”
Injection is a family of vulnerabilities:
- SQL Injection
- NoSQL Injection
- OS Command Injection
- LDAP Injection
- XPath Injection
- XSS (script injection into the browser)
- Template Injection
- XML-related injection problems
Why Injection Happens
Injection usually happens because developers do one or more of these things:
In plain English
- The developer builds commands using strings
- User input is directly inserted into those strings
- The system sends that final string to an interpreter
- The interpreter cannot tell which part is “real code” and which part came from the attacker
The General Anatomy of an Injection Bug
The vulnerable point
The dangerous moment is here:
The application takes user input and builds a command/query string with it.
That is the place where data becomes dangerous.
SQL Injection — The Classic Injection Vulnerability
5.1 What is SQL?
SQL stands for Structured Query Language. Applications use SQL to talk to relational databases like:
- MySQL
- PostgreSQL
- Microsoft SQL Server
- Oracle
- SQLite
A query might look like this:
SQLSELECT * FROM users WHERE username = 'alice' AND password = 'secret123';That means:
Find a row in the users table where username is alice and password is secret123.
5.2 What is SQL Injection?
SQL Injection happens when attacker-controlled input changes the meaning of the SQL query.
Unsafe login example
# ❌ Vulnerable example
username = request.form["username"]
password = request.form["password"]
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
result = db.execute(query)This looks innocent — but it is dangerous because user input is inserted directly into the SQL string.
Why this is bad
If the attacker provides input that contains SQL syntax, the final query changes.
The application intended to send:
- “check username and password”
But the attacker may make it send:
- “check username OR bypass the condition”
The result: authentication bypass, data theft, or worse.
# ✅ Safe example
username = request.form["username"]
password = request.form["password"]
query = "SELECT * FROM users WHERE username = ? AND password = ?"
result = db.execute(query, [username, password])Why this works
With parameterized queries:
- SQL structure is fixed first
- user input is passed separately
- the database treats input as data only
- attacker input cannot change query structure
5.4 What SQL injection can lead to
In practice, this can mean:
- logging in as someone else
- stealing all users
- changing balances
- deleting tables
- dumping customer records
- learning table names and schema
- pivoting deeper into the environment
5.5 Blind SQL Injection
Sometimes the app does not show database errors or direct data. That does not mean SQL injection is impossible.
The attacker may still infer truth from behavior:
- page changes
- yes/no response
- time delay
- different status code
What students should understand
Blind SQL injection teaches a very important lesson:
Even if the app “shows nothing useful,” the attacker may still extract data indirectly.
That is why hiding errors alone is not a fix. You must fix the root cause: unsafe query construction.
NoSQL Injection
Modern apps often use NoSQL databases like:
- MongoDB
- CouchDB
- Firebase-style query layers
Many beginners think:
“We don’t use SQL, so SQL injection is impossible — therefore we are safe.”
That is false.
The syntax may be different, but the core problem is the same:
User input changes query logic.
Example concept
A MongoDB query might expect:
- username = string
- password = string
But if the server accepts raw structured input without validation, the attacker may pass objects/operators instead of plain values.
Vulnerable example
// ❌ Vulnerable Node/Mongo example
app.post('/login', async (req, res) => {
const user = await db.collection('users').findOne({
username: req.body.username,
password: req.body.password
});
if (user) {
res.send("Logged in");
} else {
res.send("Denied");
}
});Why this can be dangerous
If input is not type-checked and sanitized, the application may accept unexpected structures rather than plain strings.
Safe version
// ✅ Safer version
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (typeof username !== "string" || typeof password !== "string") {
return res.status(400).send("Invalid input");
}
const user = await db.collection('users').findOne({
username: username,
password: password
});
if (user) {
res.send("Logged in");
} else {
res.send("Denied");
}
});Core lesson
For NoSQL too:
- validate types
- validate structure
- never trust nested operator objects from users
- use safe query building patterns
OS Command Injection
This is one of the most dangerous forms of injection.
What is it?
The application takes user input and uses it in an operating system command.
For example:
- ping a host
- convert an image
- compress a file
- run a shell script
- look up DNS
- call a system utility
If user input is inserted unsafely, the attacker may execute additional commands.
Real-life analogy
You ask an employee:
“Call this number.”
Instead of giving a number, an attacker gives:
“Call this number, then open the vault, then turn off the cameras.”
If the employee blindly obeys everything, disaster.
Vulnerable flow
Vulnerable example
# ❌ Vulnerable
import subprocess
ip = request.args.get("ip")
output = subprocess.check_output(f"ping -c 4 {ip}", shell=True)
return outputWhy this is dangerous
- shell=True means the input is interpreted by the shell
- shell syntax becomes meaningful
- attacker input may add extra commands
The application thought it was only running:
- ping -c 4 8.8.8.8
But it may end up running much more.
# ✅ Safer
import subprocess
import re
ip = request.args.get("ip")
if not re.match(r"^\d{1,3}(\.\d{1,3}){3}$", ip):
return "Invalid IP", 400
output = subprocess.check_output(["ping", "-c", "4", ip], shell=False)
return outputWhy this is safer
- input is validated
- no shell string concatenation
- arguments are passed as separate values
- shell metacharacters do not get interpreted
OS command injection impact
This is why command injection is often considered one of the worst forms of injection.
XSS — Injection into the Browser
In OWASP 2021, XSS is grouped under Injection, which makes sense because it is also a case of:
attacker-controlled input being interpreted as executable code
But here, the interpreter is not the database or shell.
It is the browser.
What is XSS?
XSS stands for Cross-Site Scripting.
It happens when attacker-controlled content is included in a web page in a way that lets the browser treat it as executable JavaScript or HTML rather than plain text.
Three major types
8.1 Reflected XSS
Attacker input comes in the request and is immediately reflected in the response.
Example pattern:
- search page prints the search term
- error page prints a parameter
- message page prints user-controlled text
8.2 Stored XSS
The attacker stores malicious content in the application:
- comment
- profile field
- forum post
- support ticket
- chat message
Later, other users load that content and their browsers execute it.
Stored XSS is often more severe than reflected XSS because it can hit many victims automatically.
8.3 DOM-Based XSS
The issue happens entirely in client-side JavaScript.
The server may not even see the malicious transformation. The browser-side code reads untrusted data and injects it unsafely into the DOM.
Vulnerable HTML example
<!-- ❌ Vulnerable -->
<div id="welcome"></div>
<script>
const name = new URLSearchParams(window.location.search).get("name");
document.getElementById("welcome").innerHTML = "Hello, " + name;
</script>Why this is dangerous
If name contains HTML/JS-capable content, innerHTML may interpret it as markup.
Safe version
<!-- ✅ Safer -->
<div id="welcome"></div>
<script>
const name = new URLSearchParams(window.location.search).get("name");
document.getElementById("welcome").textContent = "Hello, " + name;
</script>Why this is safer
textContent treats the value as text, not as HTML.
XSS impact
Other Injection Families You Should Know
Not every injection type needs a huge section, but students should know they exist.
LDAP Injection
Unsafe user input changes LDAP search filters.
XPath Injection
Unsafe input changes XML query logic.
Template Injection
Unsafe data reaches template expressions and may lead to server-side code execution.
SMTP Header Injection
Unsafe input alters email headers, recipients, or mail behavior.
The common pattern is always the same:
untrusted input is interpreted in a powerful context
Root Causes of Injection, Deeply Explained
Let’s simplify injection down to its root causes.
10.1 Mixing code and data
This is the universal root cause.
10.2 Unsafe APIs
Some APIs make unsafe behavior easy.
Examples:
- dynamic SQL string building
- shell execution with string commands
- innerHTML
- dangerous template rendering
- generic eval-like functions
10.3 Weak or missing validation
Validation does not replace parameterization, but it still matters.
For example:
- IP addresses should look like IPs
- email fields should look like emails
- sort order should be from an allowlist like asc or desc
- IDs should be numeric or UUIDs as expected
10.4 Wrong output handling
For XSS especially, the problem often is not the input alone — it is the output context.
Different contexts need different protection:
- HTML body
- HTML attribute
- JavaScript string
- CSS
- URL
- SQL parameter
- shell argument
There is no single universal “escape everything” function that solves all contexts.
That is why security teams talk about context-aware encoding.
Prevention Strategy — The Big Picture
The Golden Rule
Never let untrusted input define interpreter structure.
11.1 Prevention map
11.2 Parameterize everything that can be parameterized
This applies to:
- SQL
- NoSQL where supported through safe abstractions
- LDAP filters where safe APIs exist
- OS commands via argument arrays
- template values via safe rendering methods
11.3 Use allowlists, not vague assumptions
Bad mindset:
- “Users probably won’t enter weird input”
Good mindset:
- “Users can send anything. I will explicitly define what is acceptable.”
Examples:
- country code must be from known list
- sort field must be from allowlist
- ID must be integer/UUID
- file type must be from strict allowed set
11.4 Escape or encode for the right context
For XSS, use:
- framework auto-escaping
- textContent instead of innerHTML
- safe templating
- HTML encoding
- attribute encoding
- JavaScript-safe serialization where needed
11.5 Least privilege
If injection happens, damage should still be limited.
Examples:
- app DB user should not be DB admin
- app OS user should not be root
- service accounts should have minimum permissions
- cloud roles should be tightly scoped
1.6 Content Security Policy for XSS impact reduction
CSP does not fix XSS root causes, but it can reduce damage.
A strict CSP can:
- block inline scripts
- restrict script sources
- reduce data exfiltration options
- make exploitation harder
Example:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none';Testing for Injection
You should test injection risks systematically.
Questions to ask during review
- Does this input go into SQL?
- Does this input go into a shell command?
- Does this input end up in HTML?
- Does this input reach a template engine?
- Does this input get used in LDAP/XPath filters?
- Is code/data separation guaranteed?
- Is the escaping correct for this specific context?
A Mental Model for Students
Whenever you see input, ask this:
One-View Cheat Sheet
Injection Type Target Main Risk Best Defense
SQL Injection SQL database auth bypass, data theft, data destruction parameterized queries
NoSQL Injection NoSQL DB logic bypass, data exposure strict type/structure validation
OS Command Injection shell / system remote command execution avoid shell, use safe argument arrays
Reflected XSS browser script execution in victim browser output encoding
Stored XSS browser/users mass client-side compromise sanitize/encode output, safe rendering
DOM XSS browser DOM client-side code execution safe DOM APIs like textContent
LDAP/XPath Injection directory/XML query unauthorized lookup/access safe query APIs + validation
Template Injection template engine server-side execution safe rendering, no untrusted expressionsThe Most Important Lesson
If user input can change structure, you are in danger. If user input is treated only as data, you are much safer.