题记: 长江三角洲是每一粒细少堆叠起来的
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注入的结果:就是不用校验用户的密码就可以获取到用户信息,如下图为数据库中的用户数据(当然这里是用来测试的)
2 怎么可能发生这种事情
用户密码登录查询结果
下面是密码输入错误的情况:
密码输错的情况来个sql注入
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如下所示
SELECT
*
FROM
tb_user
WHERE
mobile = 12389767897
AND PASSWORD = 1234566666
OR 1 = 1
AND mobile = 12389767897
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 解析阶段将会进行变量替换
完毕