题记: 长江三角洲是每一粒细少堆叠起来的


​【x1】微信公众号的每日提醒 随时随记 每日积累 随心而过​

​【x2】各种系列的视频教程 免费开源 关注 你不会迷路​

​【x3】系列文章 百万 Demo 随时 复制粘贴 使用​


1 什么是 sql 注入 ???

sql 注入就是用户可以通过 输入关键字来影响原本正确的sql ,如查询用户信息

正确的 sql :

select * from t_user from moblie = '234' and passwrod = '123456'

sql注入后的现象:(这里的 moblie 与 password 都是用户输入的内容)

select * from t_user from moblie = '234' and passwrod = '123456' or 1=1

sql注入的结果:就是不用校验用户的密码就可以获取到用户信息,如下图为数据库中的用户数据(当然这里是用来测试的)

从实践的角度来回顾一下SQL注入_SQL注入

2 怎么可能发生这种事情

用户密码登录查询结果

从实践的角度来回顾一下SQL注入_SQL注入_02

下面是密码输入错误的情况:

从实践的角度来回顾一下SQL注入_安全_03

密码输错的情况来个sql注入

从实践的角度来回顾一下SQL注入_安全_04

3 Java代码拦截不到???

使用 Springboot 、Mybatis

来看看这吐血的 Java 代码 ,首先是 Controller ,这个就是定义了一个普通的请求,代码如下:

@RestController
public class UserController {

private static final Logger LOG = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;

/**
* 用户密码登录
* 通过 formdata 来传参数
*
* @param mobile 用户手机号
* @param password 用户密码
* @return
*/
@RequestMapping(value = "login", method = RequestMethod.POST)
public CommonResponse login(@RequestParam("mobile") String mobile,
@RequestParam("password") String password) {
//查询用户数据
UserBean userBean = userService.loginFromPassword(mobile, password);
//返回结果
return CommonResponse.ok(userBean);
}
}

然后在 UserService 中做基本的校验处理,代码如下:

@Service
public class UserServiceImple implements UserService {

@Autowired
public UserDao userDao;
@Override
public UserBean loginFromPassword(String mobile, String password) {

if (mobile == null || mobile.length() != 11) {
throw new RRException("请输入11位手机号");
}

if (password == null || password.length() < 6) {
throw new RRException("验证失败 请检查手机号或者密码是否输入正确");
}

UserBean userBean = userDao.selectFromPasswordTest(mobile, password);

if (userBean == null) {
throw new RRException("验证失败 请检查手机号或者密码是否输入正确");
}

return userBean;
}
}

然后对应的 Mapper 文件如下:

@Mapper
public interface UserDao {
UserBean selectFromPassword(String mobile, String password);
}

Mapper 对应的解析 xml 配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.UserDao">

<!--resultMap 映射-->
<resultMap id="userBean" type="com.example.demo.bean.UserBean">

<id property="userId" jdbcType="INTEGER" column="user_id"/>
<!--查询结果映射 -->
<!--property 为实体中的属性名称-->
<!--jdbcType 为属性对应的数据类型-->
<!--column 为select查询语句中查询对应的列名-->
<result property="id" jdbcType="INTEGER" column="id"/>
<!--例如这里的 UserBean 实体中的属性为 userName,是字符串类型,在数据库中对应的列是user_name-->
<result property="userName" jdbcType="VARCHAR" column="user_name"/>
<result property="age" jdbcType="INTEGER" column="age"/>
<result property="realName" jdbcType="VARCHAR" column="realname"/>


</resultMap>

<select id="selectFromPassword" resultMap="userBean">
select *
from tb_user
where mobile = ${mobile} AND password=${password}
</select>

</mapper>

原来是使用了 ${} 这这这 …

正确的操作应当是 :

 <select id="selectFromPassword" resultMap="userBean">
select *
from tb_user
where mobile = #{mobile} AND password=#{password}
</select>

4 怎么回事 ???

在Mybatis中动态 sql 是其主要特性之一,mybatis 提供了两种支持动态 sql 的语法:#{} 以及 $ { }, 其最大的区别则是前者方式能够很大程度防止sql注入(安全),后者方式无法防止Sql注入 。

通过 #{} 来解析处理最后生成的 SQL 如下所示:

select * from tb_user where mobile = ? AND password=? 

从实践的角度来回顾一下SQL注入_SQL注入_05

通过 ${} 来解析处理最后生成的 SQL如下所示

  SELECT
*
FROM
tb_user
WHERE
mobile = 12389767897
AND PASSWORD = 1234566666
OR 1 = 1
AND mobile = 12389767897

从实践的角度来回顾一下SQL注入_安全_06

5 为什么会这样???

在动态 SQL 解析阶段, #{ } 和 ${ } 会有不同的表现:

如这里输入的 password 值为 1234566666 or 1=1 and mobile =12389767897

5.1 使用

select * from tb_user where mobile = #{mobile} AND password=#{password}

#{ } 会解析为一个 JDBC 预编译语句(prepared statement)的参数标记点位符。

select * from tb_user where mobile = ? AND password=? 

一个 #{ } 被解析为一个参数占位符 ?

然后 值 “1234566666 or 1=1 and mobile =12389767897 ” 会被处理成 password 的值

5.2 使用 $

${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换


完毕