面试必知 mybatis防止sql注入
一.什么是sql注入:
用户通过表单提交的方式,填入与sql注释或者or 1=1等内容实现sql注入
二.JDBC防止sql注入
1.如果生成statement对象来实现凭借字符串是会被sql注入的。
concat sqlString sql = "SELECT * FROM users WHERE name ='"+ name + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
2.解决办法:通过生成PreparedStatement 对象来防止sql注入
sql = "SELECT * FROM users WHERE name= ? ";
PreparedStatement ps = connection.prepareStatement(sql);
// 参数 index 从 1 开始
ps.setString(1, name);
3.存在的问题:比如 order by、column name,不能使用参数绑定
解决:此时需要手工过滤,如通常 order by 的字段名是有限的,因此可以使用白名单的方式来限制参数值
4.PreparedStatement 防止sql注入的原理:
首先通过sql语句通过占位符的方式执行sql语句,然后再把对应的参数替换上去,sql语句就只执行了一次,替换参数不会再执行sql语句。
三.mybatis防止sql注入
1.mybatis可以通过xml文件或者注解的方式执行sql语句
1).XML文件:
<select id="getById" resultType="org.example.User">
SELECT * FROM user WHERE id = #{id}
</select>
2).注解的方式
@Mapperpublic interface UserMapper {
@Select("SELECT * FROM user WHERE id= #{id}")
User getById(@Param("id") int id);
}
2.mybatis提供两种方式进行参数替换( #{} 和 ${})
1).使用 #{} 语法时,MyBatis 会自动生成 PreparedStatement ,使用参数绑定 ( ?) 的方式来设置值,上述两个例子等价的 JDBC 查询代码如下: 可以防止sql注入
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, id);
2).使用${}其实就是进行sql的拼接,先进行sql的拼接再执行sql语句就会出现sql注入。
<select id="getByName" resultType="org.example.User">
SELECT * FROM user WHERE name = '${name}' limit 1
</select>
如果name 值为 ’ or ‘1’='1,实际执行的语句为
SELECT * FROM user WHERE name = '' or '1'='1' limit 1
3).所以mybatis推荐使用#{}来防止sql注入。
3.同样的也会出现问题:比如 order by、column name,不能使用参数绑定
其实就是如果需要用数据库表的字段来替换的话就不能用参数绑定
比如:
1). sortBy 参数值为 name ,如果使用#{}替换后会成为
ORDER BY "name"
即以字符串 “name” 来排序,而非按照 name 字段排序
2).所以需要使用${}来进行拼接,但是拼接就会出现sql注入:
代码层使用白名单的方式,限制 sortBy 允许的值,如只能为 name, email 字段,异常情况则设置为默认值 name
在 XML 配置文件中,使用 if 标签来进行判断
<select id="getUserListSortBy" resultType="org.example.User">
SELECT * FROM user
<if test="sortBy == 'name' or sortBy == 'email'">
order by ${sortBy}
</if>
</select>
因为 Mybatis 不支持 else,需要默认值的情况,可以使用 choose(when,otherwise)
<select id="getUserListSortBy" resultType="org.example.User">
SELECT * FROM user
<choose>
<when test="sortBy == 'name' or sortBy == 'email'">
order by ${sortBy}
</when>
<otherwise>
order by name
</otherwise>
</choose>
</select>
4).mybatis更多场景:
-
除了 orderby之外,还有一些可能会使用到 ${} 情况,可以使用其他方法避免,如
like语句
使用mybatis的bind标签<select id="getUserListLike" resultType="org.example.User"> <bind name="pattern" value="'%' + name + '%'" /> SELECT * FROM user WHERE name LIKE #{pattern} </select>
或者使用concat标签
<select id="getUserListLikeConcat" resultType="org.example.User"> SELECT * FROM user WHERE name LIKE concat ('%', #{name}, '%') </select>
-
in条件
使用 < foreach> 和 #{}<select id="selectUserIn" resultType="com.example.User"> SELECT * FROM user WHERE name in <foreach item="name" collection="nameList" open="(" separator="," close=")"> #{name} </foreach> </select>
-
limit语句
直接通过#{}防止sql注入