Java代码中应避免的SQL注入写法

mtain 2022年10月17日 57次浏览

Mybatis的SQL注入

Mybatis的SQL语句可以基于注解的方式写在类方法上面,更多的是以xml的方式写到xml文件。Mybatis中SQL语句需要我们自己手动编写或者用generator自动生成。编写xml文件时,MyBatis支持两种参数符号,一种是#,另一种是$。比如:

<select id="queryAll"  resultMap="resultMap">
  SELECT * FROM NEWS WHERE ID = #{id}
</select>

#使用预编译,$使用拼接SQL。

Mybatis框架下易产生SQL注入漏洞的情况主要分为以下三种:

1、模糊查询

Select * from news where title like ‘%#{title}%’

在这种情况下使用#程序会报错,新手程序员就把#号改成了$,这样如果java代码层面没有对用户输入的内容做处理势必会产生SQL注入漏洞。

正确写法:

select * from news where tile like concat(‘%’,#{title}, ‘%’)

2、in 之后的多个参数

in之后多个id查询时使用# 同样会报错,

Select * from news where id in (#{ids})

正确用法为使用foreach,而不是将#替换为$

id in
<foreach collection="ids" item="item" open="("separatosr="," close=")">
#{ids}
</foreach>

3、order by 之后

这种场景应当在Java层面做映射,设置一个字段/表名数组,仅允许用户传入索引值。这样保证传入的字段或者表名都在白名单里面。需要注意的是在mybatis-generator自动生成的SQL语句中,order by使用的也是$,而like和in没有问题。

JdbcTemplate

1. 不经过参与预编译,直接拼接

@GetMapping("/jdbcSelect")
public UserEntity jdbcSelect(@RequestParam String username){
    String sql = "select * from user where username='"+username+"'";

    UserEntity user = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
            String id = rs.getString("id");
            String name = rs.getString("username");
            String password = rs.getString("password");

            UserEntity userEntity = new UserEntity();
            userEntity.setId(id);
            userEntity.setUsername(name);
            userEntity.setPassword(password);
            return userEntity;
    });
    return user;
}
    
被SQL注入的日志
[2022-10-17 17:46:28,889 DEBUG][org.springframework.jdbc.core.JdbcTemplate query]  Executing SQL query [select * from system_user where username='admin' AND 5016=4426 AND 'XDcQ'='XDcQ']  				{@ o.s.jdbc.core.JdbcTemplate.query(427)}