HQL injection is SQL injection through Hibernate's query language. It happens when user input is concatenated into an HQL string, so the input can change the query. Named parameters stop it by sending the input as a value bound to a placeholder, never as part of the query text.
An ORM does not remove the risk
Developers often assume that using an ORM means no injection. The OWASP SQL injection cheat sheet disagrees directly. It says SQL abstraction layers like the Hibernate Query Language have the same type of injection problems, called HQL injection, and also support parameterized queries.
The Hibernate query language guide says the same in plain words. It states that it is extremely important to use parameters to pass user input to the database, and that constructing a query by concatenating HQL fragments with user input is extremely dangerous, opening the door to executing arbitrary code on the database server.
Unsafe and safe, from the OWASP example
// Unsafe: user input is part of the query text
Query unsafe = session.createQuery(
"from Inventory where productID='" + userSuppliedParameter + "'");
// Safe: the value is bound to a named parameter
Query safe = session.createQuery("from Inventory where productID=:productid");
safe.setParameter("productid", userSuppliedParameter);
In the first version, a value containing a quote character can end the string literal and add its own conditions. In the second, the query text is fixed. The value can only ever be a product ID.
The three parameter styles
The Hibernate guide describes three flavors of parameter.
| Style | Looks like | Status |
|---|---|---|
| Named | colon name, such as :name | Readable |
| Ordinal | question mark and number, such as ?1 | Supported |
| JDBC style | a bare question mark | Deprecated in the docs |
Pick named parameters. They read clearly in code review, and a reviewer can see at a glance that no concatenation is happening.
Where injection still slips in
- Sorting. A user supplied column name cannot be a parameter. Map a choice to a known property name.
- Dynamic filters. Building a where clause by appending strings in a loop. Use the criteria API or add each condition with its own named parameter.
- Native queries. SQL passed to a native query has the same rule: bind values, never concatenate.
- LIKE patterns. Bind the whole pattern value, including the percent signs you add in code.
- Stored fragments. A string built from database content can still carry input from an earlier request.
How to find it in a review
Search for the query creation methods and look at the argument. A string literal on its own is fine. A string literal with a plus sign and a variable is not. Our post on what to look for in SQL code during pull request review has a broader checklist, and ORM calls that bypass binding compares several libraries.
Safeguard scans dependencies, including Hibernate and JDBC drivers, and policy gates can hold a pull request on a failed check. A free scan is at gold.safeguard.sh/scan.
Try it yourself
Run the code scan and open a finding on a query. Look at the path from request input to query construction, which is what an HQL injection review is about.
Click through it at your own pace, or open it full screen.
Quick check
Is HQL injection the same as SQL injection?
It is the same idea applied to HQL. The input changes the structure of the query.
Do named parameters work for table names?
No. Parameters carry values. Map a fixed choice to a known entity or property name.
Which parameter style is deprecated?
The Hibernate guide marks JDBC style question mark parameters as deprecated.
Sources
Last reviewed: 19 October 2026.