Express.js SQL Injection

Critical Risk SQL Injection
sql-injectionexpressjavascriptnode-jsweb-api

What it is

SQL injection vulnerability in Express.js applications where user input from request parameters, body, and query strings is directly concatenated into SQL queries without proper parameterization.

const express = require('express');
const mysql = require('mysql2');
const app = express();

app.use(express.json());

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'app_user',
    database: 'myapp'
});

// VULNERABLE: Template literal injection
app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    const query = `SELECT * FROM users WHERE id = ${userId}`;
    connection.query(query, (err, results) => {
        res.json(results);
    });
});

// VULNERABLE: String concatenation
app.post('/login', (req, res) => {
    const { username, password } = req.body;
    const sql = "SELECT * FROM users WHERE username = '" + username +
                "' AND password = '" + password + "'";
    connection.query(sql, (err, results) => {
        res.json(results);
    });
});

// VULNERABLE: Dynamic query building
app.get('/search', (req, res) => {
    const { name, department } = req.query;
    let sql = "SELECT * FROM users WHERE 1=1";
    if (name) {
        sql += " AND name LIKE '%" + name + "%'";
    }
    if (department) {
        sql += " AND department = '" + department + "'";
    }
    connection.query(sql, (err, results) => {
        res.json(results);
    });
});

// Attack examples:
// GET /users/1 OR 1=1 --
// POST /login: username: admin' OR '1'='1' --
const express = require('express');
const mysql = require('mysql2');
const app = express();

app.use(express.json());

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'app_user',
    database: 'myapp'
});

// SECURE: Use ? placeholders instead of template literals
app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    const query = 'SELECT * FROM users WHERE id = ?';
    connection.query(query, [userId], (err, results) => {
        res.json(results);
    });
});

// SECURE: Use ? placeholders instead of concatenation
app.post('/login', (req, res) => {
    const { username, password } = req.body;
    const sql = 'SELECT * FROM users WHERE username = ? AND password = ?';
    connection.query(sql, [username, password], (err, results) => {
        res.json(results);
    });
});

// SECURE: Build params array for dynamic queries
app.get('/search', (req, res) => {
    const { name, department } = req.query;
    const params = [];
    let sql = "SELECT * FROM users WHERE 1=1";
    
    if (name) {
        sql += " AND name LIKE ?";
        params.push('%' + name + '%');
    }
    if (department) {
        sql += " AND department = ?";
        params.push(department);
    }
    
    connection.query(sql, params, (err, results) => {
        res.json(results);
    });
});

💡 Why This Fix Works

The vulnerable code uses template literals and string concatenation to build SQL queries with user input. The fixed version uses parameterized queries with ? placeholders, implements proper input validation, and uses async/await with connection pooling for better performance.

Why it happens

Using template literals to embed request parameters directly into SQL queries is a common cause of SQL injection in Express.js applications.

Root causes

Template Literal Injection in Express Routes

Using template literals to embed request parameters directly into SQL queries is a common cause of SQL injection in Express.js applications.

Preview example – JAVASCRIPT
app.get('/users/:id', (req, res) => {
    const query = `SELECT * FROM users WHERE id = \${req.params.id}`;
    connection.query(query, callback);
});

Dynamic Query Building with String Concatenation

Building SQL queries dynamically by concatenating user input from query parameters or request body creates SQL injection vulnerabilities.

Preview example – JAVASCRIPT
app.get('/search', (req, res) => {
    let sql = "SELECT * FROM users WHERE 1=1";
    if (req.query.name) {
        sql += " AND name = '" + req.query.name + "'";
    }
});

Fixes

1

Use Parameterized Queries with MySQL2

Always use parameterized queries with ? placeholders when working with MySQL2 in Express.js applications.

View implementation – JAVASCRIPT
app.get('/users/:id', async (req, res) => {
    const query = 'SELECT * FROM users WHERE id = ?';
    const [results] = await pool.execute(query, [req.params.id]);
});
2

Implement Input Validation Middleware

Create validation middleware to check parameter types, lengths, and formats before processing requests.

View implementation – JAVASCRIPT
const validateUserId = (req, res, next) => {
    const userId = parseInt(req.params.id);
    if (isNaN(userId) || userId <= 0) {
        return res.status(400).json({ error: 'Invalid user ID' });
    }
    next();
};

Detect This Vulnerability in Your Code

Sourcery automatically identifies express.js sql injection and many other security issues in your codebase.