Overview
Injection vulnerabilities in Node.js Express apps enable attackers to alter the meaning of queries or commands by injecting crafted input. In real-world scenarios this can lead to data exfiltration, unauthorized data modification, or even server compromise if injected input is executed as part of a database command or a shell operation. Since Express apps frequently expose endpoints that accept user input and pass it to databases or system commands, weak input handling directly expands the attack surface and can affect confidentiality, integrity, and availability of data and services.
This vulnerability class commonly manifests when developers build queries by concatenating strings or using template literals with user-supplied values, or when user input is passed into shell commands via child_process.exec. NoSQL stores (e.g., MongoDB) are also at risk when queries include unsanitized inputs or operators supplied by the user. Even legitimate-looking APIs can become dangerous if raw input is interpolated into code paths, enabling attackers to alter query logic, bypass auth checks, or cause unintended side effects.
Impact in production ranges from data leakage and data corruption to elevated access and remote code execution in extreme cases. The root cause is treating input as code rather than data. To reduce risk, rely on parameterized queries or safe query builders, avoid dynamic code paths driven by user input, and enforce strict input validation and least-privilege access controls across your Express services.
Code Fix Example
Node.js (Express) API Security Remediation
VULNERABLE:
const express = require('express');
const mysql = require('mysql2');
const app = express();
const pool = mysql.createPool({ /* config */ });
app.get('/user', (req, res) => {
const username = req.query.username;
// Vulnerable: string interpolation leads to SQL injection
const sql = `SELECT * FROM users WHERE username = '${username}'`;
pool.query(sql, (err, results) => {
if (err) return res.status(500).send('DB error');
res.json(results);
});
});
FIX:
const express = require('express');
const mysql = require('mysql2');
const app = express();
const pool = mysql.createPool({ /* config */ });
app.get('/user', (req, res) => {
const username = req.query.username;
// Use parameterized query to prevent injection
const sql = 'SELECT * FROM users WHERE username = ?';
pool.execute(sql, [username], (err, results) => {
if (err) return res.status(500).send('DB error');
res.json(results);
});
});