Web Security: SQL Injection (SQLi) and Parameterized Queries
One of the most classic backend security questions asked of full-stack and frontend developers integration is:
What is SQL Injection (SQLi)? How does it occur, and how do parameterized queries (prepared statements) guarantee mitigation?
SQL Injection is a vulnerability where an attacker manipulates dynamic input parameters to inject malicious SQL commands into a database query. This bypasses authentication controls and allows unauthorized access, modification, or deletion of sensitive database data.
1. How SQL Injection Occurs
Consider a simple login endpoint processing input fields from a form:
// Dangerous dynamic concatenation
const query = `SELECT * FROM users WHERE username = '${req.body.username}' AND password = '${req.body.password}'`;
db.execute(query);The Attack Vector:
If an attacker inputs the following string inside the username field:
admin' OR '1'='1
The compiled SQL query sent to the database database engine becomes:
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '...';Since '1'='1' is always true, the query bypasses password verification entirely and returns the admin profile.
2. Preventing SQLi with Parameterized Queries
The standard, most reliable way to mitigate SQL injection is using Parameterized Queries (also called Prepared Statements).
Instead of concatenating user input directly into the query template, you use placeholders (typically ? or $1 symbols) and pass the values separately to the database driver:
// Safe parameterized query
const query = 'SELECT * FROM users WHERE username = ? AND password = ?';
db.execute(query, [req.body.username, req.body.password]);How it works:
- The database driver compiles the SQL query structure before inserting user inputs.
- The inputs are treated strictly as literal parameter values, never as executable SQL commands. Even if user input contains characters like
' OR '1'='1, the database engine simply searches for a username matching that literal string.
3. Other Defense Layers
- Use ORMs (Object-Relational Mapping): Modern libraries (e.g. Prisma, Mongoose, Sequelize) construct queries using parameterized parameters automatically. Be careful not to use raw fallback query functions (like
sequelize.query()) with string interpolation. - Input Validation: Filter input values against specific types or schemas (e.g. validating ID queries are numbers) before running database calls.
- Database Least Privilege: Configure database connection credentials with minimal permissions. The application server should not connect to the database as
rootoradmin.
Key Takeaways
- Never Interpolate Strings: Avoid using template literals (
${var}) or string concatenation to build raw SQL queries. - Prepared Statements: Use database driver prepared statements to pre-compile SQL queries before applying variable values.
- Safe ORM Methods: Standard ORM database operations are safe from SQLi by default; exercise caution only with raw query bypass methods.
- Least Privilege Access: Ensure database accounts used by application APIs do not have permissions to modify database structures (DDL) or drop tables.