Sql Injection in Express
How Sql Injection Manifests in Express
SQL Injection in Express applications typically occurs when user input is directly interpolated into SQL queries without proper sanitization. Express's minimalist design means developers must explicitly handle database interactions, creating multiple opportunities for injection vulnerabilities.
The most common pattern involves concatenating user input into query strings:
const express = require('express');
const app = express();
app.use(express.json());
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.query(query, (err, results) => {
if (err) throw err;
res.json(results);
});
An attacker can exploit this by requesting /users/1 OR 1=1, causing the query to return all users. More sophisticated attacks might use 1; DROP TABLE users; to delete data.
Express middleware patterns can also introduce injection risks. Consider this example using dynamic table names:
app.get('/data/:table', (req, res) => {
const table = req.params.table;
const query = `SELECT * FROM ${table} LIMIT 10`;
db.query(query, (err, results) => {
res.json(results);
});
This allows attackers to access any table in your database by manipulating the URL parameter.
Object-relational mapping (ORM) libraries like Sequelize don't automatically prevent injection if used incorrectly. This vulnerable pattern bypasses parameterization:
app.post('/search', (req, res) => {
const { name } = req.body;
const query = `SELECT * FROM products WHERE name LIKE '%${name}%'`;
sequelize.query(query).then(results => {
res.json(results);
});
Even with ORM libraries, developers sometimes fall back to raw queries for complex operations, reintroducing injection risks.
Express-Specific Detection
Detecting SQL Injection in Express applications requires examining both the code structure and runtime behavior. middleBrick's black-box scanning approach tests unauthenticated endpoints for injection vulnerabilities without requiring source code access.
The scanner automatically identifies Express route patterns and tests for SQL injection by injecting payloads into URL parameters, query strings, and request bodies. For the vulnerable endpoint:
app.get('/users/:id', (req, res) => { ... })middleBrick would test payloads like:
1 OR 1=1- tests boolean logic injection1' OR '1'='1- tests string-based injection1; DROP TABLE users- tests command chaining1 ORDER BY 1-- -- tests for error-based detection
The scanner analyzes HTTP responses for SQL error messages, unexpected data volumes, or execution time changes that indicate successful injection.
For Express applications using template engines like Pug or EJS for dynamic SQL generation, middleBrick examines template files for unsafe interpolation patterns. The scanner also checks for common Express middleware configurations that might expose database credentials or connection details.
middleBrick's OpenAPI analysis complements black-box scanning by examining your API specification for parameters that map to database operations. When it finds endpoints with type: string parameters used in database queries, it prioritizes those for injection testing.
The CLI tool provides detailed findings with specific payloads that triggered vulnerabilities:
middlebrick scan https://api.example.com/users/1
SQL Injection detected in GET /users/:id
Severity: HIGH
Response: 200 OK (unexpected data volume)
Recommendation: Use parameterized queriesGitHub Action integration allows continuous monitoring of API endpoints as part of your deployment pipeline, catching injection vulnerabilities before production release.
Express-Specific Remediation
Express applications should use parameterized queries exclusively for database operations. Here's the secure version of the vulnerable example:
const express = require('express');
const app = express();
app.use(express.json());
const mysql = require('mysql2/promise');
const db = mysql.createPool({...});
app.get('/users/:id', async (req, res) => {
try {
const [rows] = await db.execute(
'SELECT * FROM users WHERE id = ?'
, [req.params.id]);
res.json(rows);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Database error' });
}
Notice the use of ? placeholders and passing parameters as an array. This prevents any user input from being interpreted as SQL code.
For Sequelize, use the built-in query methods with proper parameterization:
app.post('/search', async (req, res) => {
try {
const { name } = req.body;
const results = await sequelize.query(
'SELECT * FROM products WHERE name LIKE :name',
{ replacements: { name: `%${name}%` }, type: sequelize.QueryTypes.SELECT }
);
res.json(results);
} catch (err) {
res.status(500).json({ error: 'Search failed' });
}
Express middleware can enforce input validation before database queries reach the application logic:
const { body, param } = require('express-validator');
app.post('/users', [
body('email').isEmail(),
body('age').isInt({ min: 0, max: 120 }),
(req, res) => {
// Validated data is safe to use in queries
}
]);
For dynamic table access (which should generally be avoided), validate against a whitelist:
const validTables = ['users', 'products', 'orders'];
app.get('/data/:table', (req, res) => {
const table = req.params.table;
if (!validTables.includes(table)) {
return res.status(400).json({ error: 'Invalid table' });
}
db.query(`SELECT * FROM ${table} LIMIT 10`, (err, results) => {
res.json(results);
});
While this example still uses string interpolation for table names, the whitelist validation prevents arbitrary table access. For production, consider using a mapping from safe identifiers to actual table names.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |