Overview
Real-world impact: Injection vulnerabilities in Node.js Express apps can let attackers alter queries, extract data, bypass authentication, modify or delete records, or execute unintended commands if the app uses system calls or dynamic queries. In production, such flaws often arise when user input is concatenated into SQL/NoSQL query strings or used to construct commands, leading to a broad attack surface across databases and services.
How it manifests in Express: Even when using an ORM or query builder, developers may still create raw SQL strings or dynamic NoSQL queries by interpolating user input. Common patterns include building SQL with string concatenation, using template literals, or enabling operators like $where in MongoDB driven by unvalidated input. Attackers can inject SQL fragments, JSON operators, or control flow to extract data or escalate privileges.
Remediation approach: This guide describes how to fix by adopting parameterized queries, input validation, least privilege, and safe API usage. Always prefer prepared statements, avoid string interpolation, disallow shell execution, and use query builders or ORM features to separate code from data; provide a safe, auditable pattern for all data access.
Code Fix Example
Node.js (Express) API Security Remediation
/* Vulnerable pattern (SQL injection) and secure pattern (parameterized queries) with a minimal Express app */
const express = require('express');
const mysql = require('mysql2/promise');
const app = express();
const pool = mysql.createPool({ host: 'localhost', user: 'app', password: 'pass', database: 'shop' });
/* Vulnerable route: constructs SQL by concatenating user input */
app.get('/vulnerable', async (req, res) => {
const username = req.query.username;
const sql = `SELECT * FROM users WHERE username = '${username}'`;
try {
const [rows] = await pool.query(sql);
res.json(rows);
} catch (e) {
res.status(500).send('Error');
}
});
/* Secure route: uses parameterized query to prevent injection */
app.get('/secure', async (req, res) => {
const username = req.query.username;
const sql = 'SELECT * FROM users WHERE username = ?';
try {
const [rows] = await pool.execute(sql, [username]);
res.json(rows);
} catch (e) {
res.status(500).send('Error');
}
});
app.listen(3000, () => console.log('Server listening on port 3000'));