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

发布时间:2024年01月20日
//程序启动时进行表结构和数据初始化
@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>

文章来源:https://blog.csdn.net/m0_71507863/article/details/135699665
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。