SQL Injection and JPA: Query Discipline in the ORM Era

Using JPA/Hibernate does not end SQL injection risk: a query discipline guide for concatenated JPQL, native queries, ORDER BY and LIKE patterns.

Java editor window comparing a parameterized JPA query with unsafe concatenation

I use an ORM — is SQL injection still my problem?

Yes. JPA/Hibernate provides strong SQL injection protection when used with parameterized queries; but the protection comes from parameter binding, not from the ORM. Every JPQL or native query built with string concatenation is injectable, even though it passes through the ORM. SQL injection is the oldest web vulnerability still standing in the OWASP Top 10, and it survives in modern projects precisely because of the "the ORM protects us" assumption.

The one rule: data never enters the query text

The difference between the safe and unsafe pattern fits in one line. Unsafe: "select o from Order o where o.customer = '" + input + "'". Safe: "select o from Order o where o.customer = :customer" with the value bound as a parameter. The rule is identical at every layer — JPQL, the Criteria API, Spring Data derived methods and native queries: user-supplied values go into query parameters, never into query text.

Four places where the risk returns

Place

Why it is risky

Safe approach

Native queries

Raw SQL keeps the concatenation habit alive

Named parameter binding; concatenation ban

ORDER BY / sorting

Column names cannot be bound as parameters

Allowlist mapping

LIKE patterns

% and _ carry pattern meaning

Escape wildcards + bind parameters

Dynamic filter building

Conditions appended as strings

Criteria API / Specification patterns

ORDER BY: where parameters do not work

A sort column is an SQL identifier — it cannot be bound as a parameter. Appending a frontend-supplied sort=price value directly into the query is a classic injection door. The correct pattern is mapping: the incoming value is matched against a fixed allowlist in code ("price" → o.price); anything outside the list is rejected. Even with Spring Data's Pageable/Sort mechanism, validating the allowed fields remains the application's responsibility.

Defense in depth: query discipline alone is not enough

Injection defense is never left to a single layer. Input validation (type, length, format) at the application layer narrows the attack surface. At the database layer, the user your application connects with is defined with least privilege: no table ownership, no DDL, only the required operations on the required tables — so even if discipline slips somewhere, damage is contained. Keeping SQL detail out of user-facing error messages belongs to the same defense: detail to logs, generic message to users.

Detection: catching injection before production

Three practical lines: automated scanning of "query + concatenation" patterns in code review (static analysis tools catch this pattern well); boundary-value unit/integration tests for the repository layer; and specifically targeting data access endpoints in regular penetration tests. A concatenated-query finding should sit in the "non-negotiable fix" class of your static analysis policy.

Business impact: the oldest flaw, the costliest outcome

SQL injection opens directly onto the database — that is, onto all customer data; a significant share of history's large card and identity breaches belongs to this class. KVKK/GDPR notification, customer churn and contractual penalties follow in a chain. The control, by contrast, is nearly free: parameter binding is standard practice that usually also improves performance (query plan caching). Few security topics have a cost/benefit table this clear.

Frequently asked questions

Am I safe if I use the Criteria API?

Yes, as long as values are bound as parameters; but even inside Criteria it is possible to inject raw string expressions — the rule does not change: data never enters expression text.

Are stored procedures the answer?

They are safe when called with parameters; but if a procedure concatenates dynamic SQL internally, the risk moves inside. The guarantee lies in parameter discipline, not in the procedure itself.

Does input validation alone prevent injection?

No — validation narrows the surface, but bypasses are possible. The primary defense is always parameter binding; validation is the second layer.

I use NoSQL; does this concern me?

Yes — the injection principle is not about the query language but about "data mixing with commands." Embedding unvalidated input into NoSQL query objects creates equivalent risks.

Data access security checklist

  1. Codebase scanned for query concatenation; findings closed

  2. All JPQL/native queries bind parameters

  3. Sorting/column selection via allowlist mapping

  4. Wildcard escaping applied in LIKE patterns

  5. Application DB user defined with least privilege

  6. SQL error detail never leaks into user responses

  7. Concatenated-query rule enforced in static analysis

SSH Yazılım offers data access layer security audits and remediation support for Java/Spring projects. Let us review your repository layer together.