Overview
In real-world apps, injection vulnerabilities in Node.js applications, including Express, can expose sensitive data, enable unauthorized actions, or allow attackers to modify or corrupt information. While no CVEs are referenced here, this class of vulnerability has historically caused substantial data exposure and integrity issues when user input is embedded directly into queries or commands.
In Express, injection typically happens when input from req.query, req.params, or req.body is concatenated into SQL or shell commands without validation or escaping. Even when using some ORMs, developers can inadvertently revert to vulnerable patterns by mixing raw strings with interpolated values. Attackers can alter query logic, retrieve restricted data, or perform unauthorized updates.
To defend, adopt defense in depth: avoid concatenating user data into queries; use parameterized queries or prepared statements; prefer query builders or ORMs that enforce parameterization; validate and sanitize inputs; apply the principle of least privilege on the database user; and add automated tests that exercise injection paths and monitor for anomalous activity.
Code Fix Example
Node.js (Express) API Security Remediation
// Vulnerable and secure examples in a single snippet
// Vulnerable
const express = require('express');
const app = express();
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database(':memory:');
app.get('/user', (req, res) => {
const username = req.query.username;
// Vulnerable pattern: dynamic SQL via template literal (interpolated)
const sql = `SELECT * FROM users WHERE username = '${username}'`;
db.all(sql, [], (err, rows) => {
if (err) return res.status(500).send('DB error');
res.json(rows);
});
});
// Secure
app.get('/user-secure', (req, res) => {
const username = req.query.username;
db.all(`SELECT * FROM users WHERE username = ?`, [username], (err, rows) => {
if (err) return res.status(500).send('DB error');
res.json(rows);
});
});