SQL Injection:How It Works & How to Prevent It
SQL injection is one of the oldest web vulnerabilities still causing breaches today. It is also one of the easiest to prevent completely: the fix is well understood and built into every modern database driver. This guide shows how SQLi works, the main types, and how to shut it down for good. It is part of our injection series alongside cross-site scripting and prompt injection.
What Is SQL Injection?
A SQL injection vulnerability exists when an application takes user input and places it directly into a database query without separating data from code. The database cannot tell the difference between the SQL the developer intended and the SQL an attacker supplied: it executes both. This lets attackers read other users' data, bypass authentication, modify records, or in severe cases run commands on the database server.
How Does SQL Injection Work?
Consider a login query built by string concatenation. The developer expects a username and password; the attacker supplies SQL instead:
// VULNERABLE: input concatenated into the query
const q = `SELECT * FROM users
WHERE name = '${user}' AND pass = '${pass}'`;
// Attacker enters as the username: admin' --
// Resulting query:
// SELECT * FROM users WHERE name = 'admin' --' AND pass = '...'
The -- comments out the rest of the query, so the password check disappears entirely and the attacker logs in as admin. A payload like ' OR '1'='1 achieves the same by making the WHERE clause always true.
What Are the Types of SQL Injection?
| Type | How It Works | When It Is Used |
|---|---|---|
| In-band (Classic) | Results come back in the same response: error messages or UNION-based data extraction | When the app shows query output or errors |
| Blind (Inferential) | No data returned; attacker infers answers from boolean responses or time delays | When errors and output are suppressed |
| Out-of-band | Data is exfiltrated over a separate channel such as a DNS or HTTP request | When the app is slow or responses are not visible |
What Can an Attacker Do With SQL Injection?
- Authentication bypass: log in without valid credentials.
- Data theft: dump entire tables of users, passwords, payment data, or PII.
- Data tampering: modify or delete records, including audit logs.
- Privilege escalation: read or change administrative accounts.
- Remote code execution: on some database configurations, run OS commands or read server files.
How Do You Prevent SQL Injection?
1. Use Parameterized Queries (Primary Defense)
Parameterized queries (prepared statements) send the SQL and the data to the database separately, so user input is always treated as a value, never as code. This single change eliminates the vast majority of SQLi:
// SAFE: values passed as parameters, never concatenated
const sql = 'SELECT * FROM users WHERE name = $1 AND pass = $2';
const result = await db.query(sql, [user, pass]);
2. Use an ORM or Query Builder Correctly
ORMs like Prisma, Sequelize, and TypeORM parameterize queries by default. They are safe as long as you do not drop down to raw string-concatenated SQL. When you must write raw queries, still use the ORM's parameter binding rather than template literals.
3. Apply Least Privilege
The database account your app uses should have only the permissions it needs. An application that only reads and writes its own tables should not connect as a superuser. Least privilege limits the blast radius if an injection does slip through.
4. Validate Input and Use Allowlists
For inputs that cannot be parameterized: such as table or column names in dynamic queries: validate against a strict allowlist of permitted values. Never pass user input directly into identifiers. Input validation is a secondary layer; it does not replace parameterization.
SQL Injection vs XSS: What Is the Difference?
| Dimension | SQL Injection | XSS |
|---|---|---|
| Target layer | The database | The victim's browser |
| Injected code | SQL commands | JavaScript / HTML |
| Primary impact | Data theft, auth bypass | Session theft, account takeover |
| Primary defense | Parameterized queries | Output encoding + CSP |
Both are injection attacks: untrusted data treated as code: but they hit different layers and need different fixes. For the browser-side equivalent, see our XSS guide.
SQL Injection Prevention Checklist
- Use parameterized queries / prepared statements for every query that includes user input
- Never build SQL with string concatenation or template literals containing user data
- Use an ORM's parameter binding, even for raw queries
- Run the application's database account with least privilege
- Allowlist any user input used as table or column names
- Disable detailed database error messages in production
- Add SQLi tests to CI and run DAST tools such as OWASP ZAP
- Use a web application firewall as a defense-in-depth layer, not a primary control