Back to Blog
low SEVERITY7 min read

SQL Injection via String Formatting: How Parameterized Queries Save the Day

A database query in DBeaver's Altibase extension was constructing SQL statements using `String.format()` with user-controlled input, creating a classic SQL injection vulnerability. The fix replaces the unsafe string interpolation with parameterized queries using `PreparedStatement`, ensuring user input is always treated as data rather than executable SQL. This type of vulnerability is deceptively simple to introduce but equally simple to fix once you know what to look for.

O
By Orbis AppSec
Published May 28, 2026Reviewed June 3, 2026

Answer Summary

This is a SQL injection vulnerability (CWE-89) in DBeaver's Altibase extension, written in Java. The root cause is the use of `String.format()` to interpolate user-controlled input directly into SQL query strings. The fix replaces these unsafe string-formatted queries with JDBC `PreparedStatement` parameterized queries, which ensure user input is always treated as literal data and never as executable SQL syntax.

Vulnerability at a Glance

cweCWE-89
fixReplaced String.format() SQL construction with JDBC PreparedStatement parameterized queries
riskAttacker-controlled input could manipulate SQL query logic, potentially exposing or corrupting database contents
languageJava
root causeSQL queries constructed with String.format() incorporating user-supplied values without sanitization
vulnerabilitySQL Injection via String Formatting

SQL Injection via String Formatting: How Parameterized Queries Save the Day

Introduction

SQL injection has topped security vulnerability charts for decades — and for good reason. It's one of those vulnerabilities that's embarrassingly easy to introduce and potentially catastrophic to leave unpatched. Despite being well-understood, it continues to appear in codebases everywhere, from hobby projects to enterprise software.

This post examines a real SQL injection vulnerability discovered and patched in DBeaver, a popular open-source database management tool. The vulnerability lived in the Altibase database extension and involved a seemingly harmless use of String.format() to build a SQL query. Let's dig in.


The Vulnerability Explained

What Went Wrong?

The vulnerable code was located in AltibaseMetaModel.java, inside a method called getDepDDLFromDbmsMetadata. This method retrieves dependent DDL (Data Definition Language) statements from the Altibase database engine using the DBMS_METADATA package.

Here's the problematic code:

// VULNERABLE CODE
String getDepDdlQry = "SELECT dbms_metadata.get_dependent_ddl('%s', '%s', '%s') FROM DUAL";

Statement stmt = conn.createStatement();
rs = stmt.executeQuery(
    String.format(getDepDdlQry, depObjectType, sourceObject.getName(), schemaName)
);

At first glance, this looks like a straightforward query. But look closely: three user-influenced valuesdepObjectType, sourceObject.getName(), and schemaName — are being interpolated directly into the SQL string using String.format().

This means the final SQL string is constructed before it ever reaches the database. If any of those values contain SQL metacharacters, they become part of the query's logic, not just its data.

How Could It Be Exploited?

Imagine a schema name or object name that contains a single quote or a SQL comment sequence. An attacker (or even a maliciously named database object) could craft a value like:

schemaName = "PUBLIC', 'MALICIOUS_TYPE') -- "

The resulting query would become:

SELECT dbms_metadata.get_dependent_ddl('SOME_TYPE', 'SOME_OBJECT', 'PUBLIC', 'MALICIOUS_TYPE') -- ') FROM DUAL

The comment sequence (--) causes the rest of the original query to be ignored. Depending on the database's stored procedures and permissions, this could be leveraged to:

  • Exfiltrate sensitive metadata from the database catalog
  • Cause unexpected procedure calls with attacker-controlled arguments
  • Bypass access controls enforced at the application layer
  • Trigger errors that leak internal database structure information

Real-World Impact

In the context of a database management tool like DBeaver, this is particularly sensitive. The tool often connects to databases with elevated privileges — DBA accounts, schema owners, or administrative users. A successful injection in this context could have a blast radius far larger than the same vulnerability in a typical web application.

Additionally, because this code path is triggered when browsing database objects, the attack surface includes any scenario where an attacker can influence object names — for example, in shared database environments or when opening a database file provided by an untrusted source.

Severity Note: While this vulnerability is rated low severity (due to the specific conditions required to exploit it), SQL injection vulnerabilities should never be dismissed. The "low" rating reflects exploitability constraints, not the potential damage if those constraints are met.


The Fix

What Changed?

The fix is clean, minimal, and follows industry best practices. Here's the patched code:

// FIXED CODE
String getDepDdlQry = "SELECT dbms_metadata.get_dependent_ddl(?, ?, ?) FROM DUAL";

PreparedStatement pstmt = conn.prepareStatement(getDepDdlQry);
pstmt.setString(1, depObjectType);
pstmt.setString(2, sourceObject.getName());
pstmt.setString(3, schemaName);
rs = pstmt.executeQuery();

Before vs. After

Aspect Before (Vulnerable) After (Fixed)
Query construction String.format() with %s placeholders SQL ? parameter placeholders
Statement type Statement PreparedStatement
Input handling Interpolated into SQL string Passed as typed parameters
Injection risk ✅ Present ❌ Eliminated

Why Does This Fix Work?

Parameterized queries (also called prepared statements) work by separating the SQL code from the data. When you write:

String getDepDdlQry = "SELECT dbms_metadata.get_dependent_ddl(?, ?, ?) FROM DUAL";
PreparedStatement pstmt = conn.prepareStatement(getDepDdlQry);

The SQL structure is sent to the database first, and the database parses and compiles it as a query template. The ? placeholders are slots for data — nothing more. When you subsequently call:

pstmt.setString(1, depObjectType);

The JDBC driver ensures the value is properly escaped and transmitted as a string literal, not as SQL syntax. No matter what depObjectType contains — single quotes, semicolons, comment sequences — it will always be treated as a string value, never as SQL code.

This is fundamentally different from string formatting, where the database sees a single pre-built string and has no way to distinguish between "code the developer wrote" and "data the user provided."


Prevention & Best Practices

1. Always Use Parameterized Queries

This is the golden rule. Whenever you're incorporating external data into a SQL query, use parameterized queries or prepared statements. This applies to:

  • User input from forms or APIs
  • Values read from files or external systems
  • Data retrieved from other database queries
  • Object names, schema names, and identifiers passed between systems
// ❌ Never do this
String query = "SELECT * FROM " + tableName + " WHERE id = " + userId;

// ✅ Always do this
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setInt(1, userId);

2. Be Extra Careful with Metadata Queries

Queries involving database metadata (schema names, object names, DDL operations) are often overlooked in security reviews because they feel "internal." But as this vulnerability demonstrates, these code paths can be just as dangerous as user-facing query builders.

3. Use an ORM or Query Builder

Object-Relational Mappers (ORMs) like Hibernate, jOOQ, or Spring Data JPA handle parameterization automatically in most cases. They reduce the surface area for injection vulnerabilities by abstracting raw SQL construction.

// jOOQ example - parameterization handled automatically
Result<Record> result = create
    .select()
    .from(TABLE)
    .where(SCHEMA_NAME.eq(schemaName))
    .fetch();

4. Conduct Regular Code Reviews with Security Focus

SQL injection vulnerabilities often slip through because they look correct. Establishing a checklist for code reviews that specifically calls out:

  • Any use of String.format(), StringBuilder, or + concatenation in SQL contexts
  • Any use of Statement instead of PreparedStatement
  • Any query construction involving external or user-influenced data

5. Use Static Analysis Tools

Tools like Semgrep, SonarQube, SpotBugs with FindSecBugs, and Checkmarx can automatically detect SQL injection patterns during CI/CD pipelines. The rule that caught this vulnerability (insecure-document-method) is an example of exactly this kind of automated detection.

# Example Semgrep rule concept for Java SQL injection
rules:
  - id: java-sql-injection-string-format
    pattern: |
      String $QUERY = String.format($TEMPLATE, ...);
      $STMT.executeQuery($QUERY);
    message: Potential SQL injection via string formatting
    severity: WARNING

6. Follow Security Standards

This vulnerability maps to well-established security standards:

  • OWASP Top 10 (2021): A03 - Injection — SQL injection remains one of the most critical web application security risks
  • CWE-89: Improper Neutralization of Special Elements used in an SQL Command — The canonical classification for SQL injection
  • SANS Top 25 — SQL injection consistently appears on this list of most dangerous software errors

Key Takeaways

Let's recap what we learned from this vulnerability and its fix:

  1. String.format() and SQL don't mix. Whenever you see SQL being built with string interpolation, treat it as a red flag.

  2. PreparedStatement is the right tool. In Java, there's almost never a good reason to use a raw Statement when executing queries with external data.

  3. "Internal" code paths need security review too. This vulnerability wasn't in a login form or a search box — it was in a metadata retrieval utility. Security doesn't stop at the "user-facing" boundary.

  4. Automated tools catch real bugs. This fix was identified through automated static analysis, demonstrating the value of integrating security scanning into your development workflow.

  5. The fix was simple. Ten lines of code changed, zero functionality lost, one vulnerability eliminated. Security improvements don't always require architectural overhauls — sometimes they're just about using the right API.


Conclusion

SQL injection is a vulnerability with a simple root cause and an equally simple cure: never trust string concatenation or formatting for SQL construction; always use parameterized queries. The vulnerability fixed here in DBeaver's Altibase extension is a textbook example of how easy it is to make this mistake, and how straightforward it is to correct.

As developers, we owe it to our users and our systems to treat every database query as a potential attack surface. The good news is that the tools to write safe SQL have existed for decades — we just need to use them consistently.

If you're working with JDBC in Java, make PreparedStatement your default. If you're using another language or framework, learn its equivalent parameterization mechanism. Your future self — and your security team — will thank you.


Found a vulnerability in your codebase? Automated security scanning can help identify issues like this one before they reach production. Consider integrating tools like Semgrep, SonarQube, or GitHub's CodeQL into your CI/CD pipeline.

Frequently Asked Questions

What is SQL injection via string formatting?

SQL injection via string formatting occurs when user-controlled input is embedded directly into a SQL string using string interpolation (like Java's String.format()), allowing attackers to break out of the intended query structure and inject arbitrary SQL commands.

How do you prevent SQL injection in Java?

Use JDBC PreparedStatement with parameterized queries (? placeholders) instead of concatenating or formatting user input into SQL strings. This ensures the database driver always treats input as data, never as SQL syntax.

What CWE is SQL injection?

SQL injection is classified as CWE-89: Improper Neutralization of Special Elements used in an SQL Command.

Is input sanitization enough to prevent SQL injection in Java?

No. Input sanitization (like escaping quotes) is fragile and error-prone. Parameterized queries via PreparedStatement are the correct and robust solution because they structurally separate SQL code from data, making injection impossible regardless of input content.

Can static analysis detect SQL injection via String.format()?

Yes. Static analysis tools like Semgrep can trace tainted data from user-controlled sources through string formatting calls to SQL execution sinks, flagging exactly this pattern. Orbis AppSec detected this vulnerability automatically using this approach.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #39705

Related Articles

critical

How Unsafe Fall-Through in getWhereConditions Happens in Sequelize and How to Fix It

A critical vulnerability in Sequelize (CVE-2023-22579) allowed attackers to inject raw SQL through an unsafe fall-through in the `getWhereConditions` function when parentheses were used in query attributes. Upgrading from version 6.26.0 to 6.29.0 closes this attack vector by tightening how raw attributes are handled. Any Node.js application using Sequelize for database queries should treat this upgrade as an urgent security priority.

high

How SQL Injection happens in Python BigQuery connectors and how to fix it

A high-severity SQL injection vulnerability was discovered in a BigQuery connector's query-building logic, where Python f-strings interpolated user-controlled identifiers—project_id, dataset_id, table_id, and timestamp_column—directly into SQL without validation. An attacker with control over connector configuration could inject arbitrary BigQuery SQL, including destructive statements. The fix introduces strict allowlist-based identifier validation using compiled regular expressions before any S

critical

How SQL Injection happens in PHP PDO queries and how to fix it

A critical SQL injection vulnerability was discovered in the `getOfficialContests()` method of ContestRepository.php, where the `$site_id` parameter was directly interpolated into a SQL query string instead of using prepared statements. This vulnerability allowed attackers to inject arbitrary SQL commands and potentially access or manipulate the entire contest database. The fix replaced `pdo->query()` with `pdo->prepare()` and proper parameter binding.

critical

How SQL Injection Happens in CSV-to-SQL Converters and How to Fix It

A critical SQL injection vulnerability was discovered in the `csv2sql()` function in `src/data/converter/csv.js`, where CSV data and table names were directly interpolated into SQL INSERT statements without sanitization. The fix implements input validation through identifier sanitization and proper value escaping, eliminating the attack surface while preserving legitimate functionality.

critical

How SQL injection happens in Node.js string interpolation and how to fix it

A critical SQL injection vulnerability was discovered in the `getScript()` method of `src/core/statistics.js`, where the `metadata_id` variable was directly interpolated into DELETE and UPDATE SQL statements without any validation. An attacker controlling this parameter could inject malicious SQL payloads to delete entire tables or exfiltrate sensitive data. The fix implements strict input validation using `parseInt()` and regex patterns to ensure only safe values reach the database queries.

critical

How SQL Injection happens in Node.js MySQL queries and how to fix it

A critical SQL injection vulnerability was discovered in `divisible_asset.js` where `message_index` and `output_index` values from external payment data were directly interpolated into SQL queries without proper escaping. This fix applies `conn.escape()` to these parameters, preventing attackers from manipulating database queries through crafted payment elements.