Sql Injection in Express with Api Keys
Sql Injection in Express with Api Keys — how this specific combination creates or exposes the vulnerability
SQL Injection in an Express API that uses API keys can occur when user-controlled data is concatenated into SQL queries, even when an API key is required for access control. The API key provides authentication—confirming who is making the request—but it does not enforce authorization or input validation. If the API key is valid but the endpoint builds SQL dynamically using string interpolation or concatenation, an attacker can supply malicious input that alters query logic.
For example, consider an endpoint that identifies a user by ID and returns profile information. If the ID is taken directly from request parameters and appended to a query string, an attacker can inject SQL fragments regardless of a valid API key. The API key check passes, but the database executes unintended commands, potentially bypassing row-level restrictions or extracting sensitive data.
In the context of middleBrick’s 12 security checks, this scenario maps to Authentication (API key presence), Input Validation (payload structure), and BOLA/IDOR (inadequate ownership checks). An unauthenticated attacker does not need to bypass the API key mechanism; they exploit the lack of input sanitization after authentication. Real-world patterns include using concatenated strings with template literals or query builders that do not enforce parameterized inputs. Without parameterized queries or strict schema validation, the API remains vulnerable despite having an API key gate.
A concrete risk pattern is when an endpoint reads an API key from headers, verifies it against a store, and then uses request parameters in a raw SQL string. This can lead to classic injection payloads such as ' OR 1=1 -- to bypass intended filters. The API key does not mitigate injection because it is checked separately from query construction. middleBrick’s detection of SQL Injection in Express with API keys focuses on runtime behavior: it observes whether inputs are sanitized or parameterized after authentication and whether error messages leak schema details.
Additionally, if the API key is leaked in logs, URLs, or error responses, it may aid an attacker in crafting targeted injection attempts. Proper handling requires treating API keys as credentials, storing them securely, and ensuring downstream SQL usage does not depend on raw user input. Middle-tier risks also arise when developers assume API keys alone are sufficient for data isolation, neglecting row-level policies and strict input validation.
Api Keys-Specific Remediation in Express — concrete code fixes
To remediate SQL Injection in Express when using API keys, enforce parameterized queries and strict input validation independent of authentication. API keys should be validated early in the request lifecycle, but they must not be conflated with safe data handling. Below are concrete examples demonstrating secure patterns.
Example 1: Secure endpoint with API key and parameterized queries
const express = require('express');
const mysql = require('mysql2/promise');
const app = express();
const pool = mysql.createPool({
host: 'localhost',
user: 'app_user',
password: 'strong_password',
database: 'app_db',
waitForConnections: true,
connectionLimit: 10,
});
const VALID_API_KEYS = new Set(['abc123', 'def456']);
app.get('/profile', async (req, res) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey || !VALID_API_KEYS.has(apiKey)) {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = req.query.userId;
if (!userId || !/^[0-9]+$/.test(userId)) {
return res.status(400).json({ error: 'Invalid input' });
}
try {
const [rows] = await pool.execute(
'SELECT id, username, email FROM users WHERE id = ?',
[parseInt(userId, 10)]
);
if (rows.length === 0) {
return res.status(404).json({ error: 'Not found' });
}
res.json(rows[0]);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Example 2: Using environment variables for API keys and prepared statements
const express = require('express');
const { Pool } = require('pg');
const app = express();
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
const VALID_API_KEYS = new Set(process.env.API_KEYS?.split(',') || []);
app.get('/search', async (req, res) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey || !VALID_API_KEYS.has(apiKey)) {
return res.status(401).json({ error: 'Unauthorized' });
}
const term = req.query.q;
if (typeof term !== 'string' || term.trim().length === 0) {
return res.status(400).json({ error: 'Missing query' });
}
try {
const query = {
text: 'SELECT id, title, description FROM items WHERE title ILIKE $1 LIMIT 10',
values: [`%${term}%`],
};
const { rows } = await pool.query(query);
res.json(rows);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Key practices
- Validate and sanitize all inputs independently of API key checks.
- Use parameterized queries or prepared statements to separate SQL logic from data.
- Restrict API key scope and rotate them regularly; treat them like passwords.
- Return generic error messages to avoid leaking schema or execution paths.
- Apply regex or type checks to ensure inputs conform to expected formats.
These steps ensure that API keys fulfill their intended role—authentication—without introducing SQL Injection risks through improper query construction.
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 |