梦の博客 欢迎来到我的破站
歌曲封面 未知作品

网站已运行 362 天 13 小时 47 分

Powered by Typecho & Sunny

2 online · 62 ms

Title

JAVAweb代码审计之sql注入漏洞

云梦

·

Article

SQL注入漏洞是对数据库进行的一种攻击方式。
其主要形成方式是在数据交互中,前端数据通过后台在对数据库进行操作时,由于没有做好安全防护,导致攻击者将恶意代码拼接到请求参数中,被当做SQL语句的一部分进行执行,最终导致数据库被攻击。可以说所有可以涉及到数据库增删改查的系统功能点都有可能存在SQL注入漏洞。虽然现在针对SQL注入的防护层出不穷,但大多情况下由于开发人员的疏忽或特定的使用场景,还是会存在SQL注入漏洞的代码。

一、Jdbc中SQL注入

1、动态拼接

SQL语句动态拼接导致的SQL注入漏洞是先前最为常见的场景。
其主要原因是后端代码将前端获取的参数动态直接拼接到SQL语句中使用 java.sql.Statement 执行
SQL语句从而导致SQL注入漏洞的出现。
在这里关键点有两个:①、动态拼接参数。②、使用 java.sql.Statement 执行SQL语句。

1.1、java.sql.Statement

Statement 对象用于执行一条静态的 SQL 语句并获取它的结果。
createStatement() :创建一个 Statement 对象,之后可使用 executeQuery() 方法执行SQL语句。
executeQuery(String sql) 方法:执行指定的 SQL 语句,返回单个 ResultSet 对象。

2、错误的预编译

在动态拼接中是使用Statement执行SQL语句。如果使用 PreparedStatement 预编译参数化查询是能够
有效防止SQL注入的。
但如果没有正确的使用 PreparedStatement 预编译还是会存在SQL注入风险的。

2.1、java.sql.PreparedStatement

PreparedStatement是继承Statement的子接口。
PreparedStatement 会对SQL语句进行预编译,不论输入什么,经过预编译后全都以字符串来执行SQL
语句。
PreparedStatement会先使用 ? 作为占位符将SQL语句进行预编译,确定语句结构,再传入参数进行执
行查询。如下述代码:

♾️ java 代码:
String sql = "select * from users where username = ?";
PreparedStatement preparestatement = conn.prepareStatement(sql);
preparestatement.setString(1, username);

3.order by注入

在SQL语句中, order by 语句用于对结果集进行排序。 order by 语句后面需要是字段名或者字段位
置。
在使用 PreparedStatement 预编译时,会将传递任意参数使用单引号包裹进而变为了字符串。
如果使用预编译方式执行 order by 语句,设置的字段名会被人为是字符串,而不在是字段名。
因此,在使用 order by 时,就不能使用 PreparedStatement 预编译了。

二、Mybatis

1、Mybatis中 #{} 和 ${} 区别
在Mybatis中拼接SQL语句有两种方式:一种是占位符 #{} ,另一种是拼接符 ${} 。
占位符 #{} :对传入的参数进行预编译转义处理。类似JDBC中的 PreparedStatement 。
比如: select from user where id = #{number} ,如果传入数值为1,最终会被解析成 select from user where id = "1" 。
拼接符 ${} :对传入的参数不做处理,直接拼接,进而会造成SQL注入漏洞。
比如: select * from user where id = ${number} ,如果传入数值为1,最终会被解析成
select * from user where id = 1 。
#{} 可以有效防止SQL注入漏洞。 ${} 则无法防止SQL注入漏洞。
因此在我们对JavaWeb整合Mybatis系统进行代码审计时,应着重审计SQL语句拼接的地方。
除非开发人员的粗心对拼接语句使用了 ${} 方式造成的SQL注入漏洞。
在Mybatis中有几种场景是不能使用预编译方式的,比如: order by 、 in , like 。

2、order by注入

ORDER BY语句 :用于对查询结果的排序,asc为升序,desc为降序。默认为升序。
比如: select * from users order by username asc;
与JDBC预编译中order by注入一样,在 order by 语句后面需要是字段名或者字段位置。因此也不能使
用Mybatis中预编译的方式。

3、in注入

IN语句 :常用于where表达式中,其作用是查询某个范围内的数据。
比如: select * from where field in (value1,value2,value3,…);
如上所示,in在查询某个范围数据是会用到多个参数,在Mybtis中如果直接使用占位符 #{} 进行查询会
将这些参数看做一个整体,查询会报错。
因此很多开发人员可能会使用拼接符 ${} 对参数进行查询,从而造成了SQL注入漏洞。
比如: select * from users where id in (${params})
正确的做法是需要使用foreach配合占位符 #{} 实现IN查询。比如:

♾️ java 代码:
<!-- where in 查询场景 -->
<select id="select" parameterType="java.util.List" resultMap="BaseResultMap">
SELECT *
FROM user
WHERE name IN
<foreach collection="names" item="name" open="(" close=")" separator=",">
#{name}
</foreach>
</select>

4、like注入

LIKE 语句 :在一个字符型字段列中检索包含对应子串的。
比如: select * from users where username like admin
使用like语句进行查询时如果使用占位符 #{} 查询时程序会报错(大家可自行调试)。
比如: select * from users where username like '%#{username}%'
因此经验不足的开发人员可能会使用拼接符 ${} 对参数进行查询,从而造成了SQL注入漏洞。
比如: select * from users where username like '%${username}%'
下面代码是正确的做法,可以防止SQL注入漏洞,如下。

♾️ java 代码:
SELECT * FROM users WHERE name like CONCAT("%", #{name}, "%")

三、SQL注入漏洞修复

原文:https://gist.github.com/retanoj/5fd369524a18ab68a4fe7ac5e0d121e8

3.1:表,字段名称

(Select, Order by, Group by 等)

♾️ java 代码:
// 插入数据用户可控时,应使用白名单处理
// example for order by

String orderBy = "{user input}";
String orderByField;
switch (orderBy) {
    case "name":
        orderByField = "name";break;
    case "age":
        orderByField = "age"; break;
    default:
        orderByField = "id";
}

3.2:JDBC

♾️ java 代码:
String name = "foo";

// 一般查询场景
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement pre = conn.prepareStatement(sql);
pre.setString(1, name);
ResultSet rs = pre.executeQuery();

// like 模糊查询场景
String sql = "SELECT * FROM users WHERE name like ?";
PreparedStatement pre = conn.prepareStatement(sql);
pre.setString(1, "%"+name+"%");
ResultSet rs = pre.executeQuery();

// where in 查询场景
String sql = "select * from user where id in (";
Integer[] ids = new Integer[]{1,2,3};

StringBuilder placeholderSql = new StringBuilder(sql);
for(int i=0,size=ids.length;i<size;i++) {
    placeholderSql.append("?");
    if (i != size-1) {
        placeholderSql.append(",");
    }
}
placeholderSql.append(")");

PreparedStatement pre = conn.prepareStatement(placeholderSql.toString());
for(int i=0,size=ids.length;i<size;i++) {
    pre.setInt(i+1, ids[i]);
}
ResultSet rs = pre.executeQuery();

3.3:Spring-JDBC

♾️ java 代码:
JdbcTemplate jdbcTemplate = new JdbcTemplate(app.dataSource());

// 一般查询场景
String sql = "select * from user where id = ?";
Integer id = 1;
UserDO user = jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(UserDO.class), id);

// like 模糊查询场景
String sql = "select * from user where name like ?";
String like_name = "%" + "foo" + "%";
UserDO user = jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(UserDO.class), like_name);

// where in 查询场景
NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(app.dataSource());

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("names", Arrays.asList("foo", "bar"));

String sql = "select * from user where name in (:names)";
List<UserDO> users = namedJdbcTemplate.query(sql, parameters, BeanPropertyRowMapper.newInstance(UserDO.class));

3.4:Mybatis XML Mapper

♾️ java 代码:
<!-- 一般查询场景 -->
<select id="select" parameterType="java.lang.String" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name = #{name}
</select>

<!-- like 查询场景 -->
<select id="select" parameterType="java.lang.String" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name like CONCAT("%", #{name}, "%")
</select>

<!-- where in 查询场景 -->
<select id="select" parameterType="java.util.List" resultMap="BaseResultMap">
    SELECT *
    FROM user
    WHERE name IN
    <foreach collection="names" item="name" open="(" close=")" separator=",">
      #{name}
    </foreach>
</select>

3.5:Mybatis Criteria

♾️ java 代码:
public class UserDO {
    private Integer id;
    private String name;
    private Integer age;
}

public class UserDOExample {
    // auto generate by Mybatis
}

UserDOMapper userMapper = session.getMapper(UserDOMapper.class);
UserDOExample userExample = new UserDOExample();
UserDOExample.Criteria criteria = userExample.createCriteria();

// 一般查询场景
criteria.andNameEqualTo("foo");

// like 模糊查询场景
criteria.andNameLike("%foo%");

// where in 查询场景
criteria.andIdIn(Arrays.asList(1,2));

List<UserDO> users = userMapper.selectByExample(userExample);
现在已有 0 条评论,0 人点赞
Comment
发表
搜 索 消 息 足 迹
你还不曾留言过..
你还不曾留下足迹..
博主