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

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


【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注入的结果:就是不用校验用户的密码就可以获取到用户信息,如下图为数据库中的用户数据(当然这里是用来测试的)
在这里插入图片描述

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 解析阶段将会进行变量替换


完毕

早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页