How to Prevent SQL Injection in MyBatis and Other ORM Frameworks

This article explains the principles behind SQL injection, common pitfalls in MyBatis and other ORM tools, and provides concrete safe coding patterns, configuration tips, and code examples to help developers eliminate injection vulnerabilities in their persistence layer.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
How to Prevent SQL Injection in MyBatis and Other ORM Frameworks

This article explains common SQL coding defects, their underlying principles, causes, and mitigation strategies, with a focus on MyBatis and other ORM frameworks.

SQL Injection Vulnerability Principles and Causes

SQL injection occurs when external input is mistakenly treated as SQL code. The most effective solution is to use prepared statements. Executing an SQL statement involves three basic steps: code semantic analysis, execution‑plan generation, and obtaining the result set.

An SQL statement consists of code and data, for example:

SELECT id, name, phone FROM userTable WHERE name = 'xiaoming';

In a prepared statement (MyBatis example) the SQL is analyzed with placeholders first:

SELECT id, name, phone FROM userTable WHERE name = #{name};

During execution the data "xiaoming" is bound to the placeholder, preventing it from being parsed as code.

Historically developers used raw JDBC and concatenated user input directly into SQL, which easily creates injection holes. Modern projects are required to use ORM frameworks such as MyBatis.

Direct Use of MyBatis

Common Pitfalls

MyBatis supports two placeholder syntaxes: {} (string concatenation) and #{} (prepared‑statement placeholder). Using {} passes the parameter unchanged, leading to injection. For example:

SELECT id, name, phone FROM userTable WHERE name = '${name}';

When name=xiaoming is supplied, the final SQL becomes

SELECT id, name, phone FROM userTable WHERE name = 'xiaoming';

, which is vulnerable.

In ORDER BY clauses the column name must be injected with ${} because it is part of the SQL syntax, but this can cause errors if not validated.

Correct Practices

Use #{} for values and validate any ${} usage. For ORDER BY you can:

Apply conditional logic in the mapper XML to choose a safe column.

<select id="getUserAndOrder" resultType="Emp" parameterType="Emp">
    select * from users where id < #{id}
    <choose>
        <when test="order == \"name\"">order by name</when>
        <when test="order != \"age\"">order by age</when>
        <otherwise>order by id</otherwise>
    </choose>
</select>

Additionally, filter the orderByClause with a whitelist (letters, digits, underscore) using a regular expression:

keyword = keyword.replaceAll("[^a-zA-Z0-9_\\s+]", "");

Never rely on black‑list filtering.

MyBatis‑Generator Safe Usage

Dynamic Statement Support

MyBatis‑Generator can generate dynamic SQL modules. If developers concatenate raw input into ${criterion.condition}, injection occurs. The correct approach is to let the generator handle placeholders and bind values with #{}:

public void addKeywordTo(String keyword, UserExample example) {
    example.or().andDisplayNameLike("%" + keyword + "%");
    example.or().andOrgLike(keyword + "%");
    example.or().andStatusLike("%" + keyword + "%");
    example.or().andIdLike("%" + keyword + "%");
}

Generated XML uses #{criterion.value} for data, keeping the code part separate.

Order By in Generated XML

The generator inserts order by ${orderByClause}. Without additional validation this is vulnerable, so the parameter must be filtered or the clause removed when not needed.

Other ORM Frameworks

Hibernate

Hibernate maps database tables to Java POJOs and uses HQL. Direct string concatenation in HQL creates injection risks:

List<Student> list = session.createQuery("FROM Student s WHERE s.stuId = " + stuId).list();

Use placeholders or named parameters instead:

List<Student> list = session.createQuery("FROM Student s WHERE s.stuId = :stuId").setParameter("stuId", stuId).list();

JPA

JPA follows the same principle. Unsafe concatenation leads to injection. Use prepared statements:

String sql = "SELECT username FROM users WHERE id = ?";
Query query = em.createNativeQuery(sql);
query.setParameter(1, id);
String username = (String) query.getSingleResult();

Conclusion

Key takeaways:

There are many persistence‑layer components; each has its own security considerations.

Misunderstanding tool usage is the main cause of vulnerabilities.

Dynamic code generation in plugins makes simple pattern matching insufficient; comprehensive analysis of ORM behavior is required.

Reference links:

https://www.anquanke.com/post/id/190170#h2-3

https://www.cnblogs.com/alka1d/p/11582993.html

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMyBatisSQL injectionORM security
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.