CWE-89 is SQL injection. The product builds all or part of an SQL command from externally influenced input, without neutralizing, or while incorrectly neutralizing, special elements that could change the intended command. Without sufficient removal or quoting of SQL syntax in user-controllable input, the query can treat that input as SQL instead of ordinary data.
It affects anything that talks to a SQL database with values from outside: login forms, search boxes, APIs, reports, chatbots and agents that write queries for users.
How it happens
The mistake is joining text. A query is assembled from a fixed part and a user part, and the database reads the result as one statement.
# Vulnerable
cur.execute("SELECT * FROM users WHERE name = '" + name + "'")
# name = "x' OR '1'='1" changes the meaning of the query
// Vulnerable
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(
"SELECT * FROM users WHERE name = '" + name + "'");
// Vulnerable
db.query("SELECT * FROM users WHERE name = '" + req.query.name + "'");
The fix in all three languages is the same idea: send the query and the values separately.
cur.execute("SELECT * FROM users WHERE name = %s", (name,))
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE name = ?");
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
db.query("SELECT * FROM users WHERE name = ?", [req.query.name]);
The database receives the structure first and the values second, so the values can never become syntax. The placeholder style varies by driver, so check your library.
The observed examples show that this is current:
- CVE-2024-6847: SQL injection in an AI chatbot via a conversation message.
- CVE-2025-26794: SQL injection in an email agent through a SQLite integration.
- CVE-2023-32530: SQL injection in a security product dashboard using crafted certificate fields.
- CVE-2021-42258: SQL injection in time and billing software, exploited in the wild according to the CISA KEV catalog.
See CVE-2024-6847, CVE-2025-26794, CVE-2023-32530 and CVE-2021-42258. Note that the input came from a chat message and from certificate fields, not only from forms. Any value that ends up in a query counts.
What an attacker gains
MITRE lists five kinds of impact.
- Confidentiality. SQL databases generally hold sensitive data, so loss of confidentiality is a frequent problem.
- Authentication. If SQL checks user names and passwords, it may be possible to connect as another user with no knowledge of the password.
- Access control. If authorization data lives in a database, injection may change it.
- Integrity. Just as it is possible to read data, it is possible to modify or delete it.
- Code execution. Adversaries could execute system commands, typically by changing the statement to redirect output to a file that can then be executed.
How to find it
MITRE lists these methods.
- Automated static analysis. Many tools use data flow or constraint techniques to reduce false positives. They may not recognize when proper validation is in place, and may miss custom code.
- Automated dynamic analysis. Fuzzing, robustness testing and fault injection with diverse inputs. The software may slow down, but it should not become unstable or give wrong results.
- Manual analysis. Useful, but it may not reach the coverage you need in limited time, because the attack surface covers every input.
- Bytecode and binary analysis, which MITRE cites as cost effective when paired with source weakness analysis.
A quick manual pass: search for query calls where the SQL text is built with concatenation, string formatting or template literals.
Click through it at your own pace, or open it full screen.
How to fix it
From the record, roughly in order of strength:
- Use parameterization. Use structured mechanisms that enforce separation between data and code: prepared statements, parameterized queries or stored procedures with strongly typed parameters. Do not build and execute query strings inside stored procedures with "exec" or similar, because that brings the risk back.
- Use a vetted library or framework. MITRE suggests persistence layers such as Hibernate or Enterprise Java Beans, which can give significant protection if used properly.
- Give the database account least privilege. Applications rarely need to run as the database administrator. Create isolated accounts with minimum privileges, such as read and write on their own data only, and use the strictest permissions on database objects, such as execute-only on stored procedures.
- Escape when you cannot parameterize. Properly quote arguments and escape special characters. Use features that exist in the database or language, such as Oracle's DBMS_ASSERT package or MySQL's mysql_real_escape_string for C and PHP.
- Validate input with an allowlist. MITRE says proper encoding, escaping and quoting is the most effective solution, and validation is defense in depth, because names like O'Reilly are legitimate yet contain an apostrophe. When feasible, disallow meta-characters entirely, since later processes may neglect to escape them.
- Map fixed values. When the set of acceptable values is known, map fixed IDs to the actual values and reject all other input. This is also the only safe route for parts of a query that cannot be parameterized, such as column names or sort direction.
- Keep error messages minimal. Messages that reveal the structure of a query help attackers tailor their strings. Log details instead, and keep passwords out of logs.
- Repeat client checks on the server.
- Consider an application firewall as an emergency or defense in depth measure. MITRE rates it moderate: it may not cover every input, can be bypassed with malformed input and may reject legitimate requests.
- In PHP, do not use register_globals.
SQL injection also comes in through libraries: an ORM helper, a reporting plugin or an admin tool can build queries unsafely in code you did not write. The free in-browser dependency scan at gold.safeguard.sh/scan needs no login, keeps your file on your machine and covers up to 100 dependencies. Reachability analysis then shows whether your code calls the affected function, which helps you order the work.
Quick check
Is escaping quotes enough?
It is a fallback, not the goal. Parameterized queries keep data and code separate by design and are the primary fix. Escaping depends on getting it right at every call.
Why is a database account with limited privileges useful?
If an injection still gets through, the attacker is limited to what that account can do, instead of getting the whole database.
Can a stored procedure still be injectable?
Yes, if it builds and executes a query string with exec or similar inside the procedure. MITRE warns against that.