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.
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
Codebase scanned for query concatenation; findings closed
All JPQL/native queries bind parameters
Sorting/column selection via allowlist mapping
Wildcard escaping applied in LIKE patterns
Application DB user defined with least privilege
SQL error detail never leaks into user responses
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.