5

经销商技术部-防SQL注入实践

 1 year ago
source link: https://netsecurity.51cto.com/article/715730.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

经销商技术部-防SQL注入实践-51CTO.COM

经销商技术部-防SQL注入实践 原创 精选
作者: 之家技术 2022-08-05 16:47:15
网络安全现在越来越受到重视,其中后端应用最常见的安全漏洞就是SQL注入。SQL注入攻击是通过操作输入来修改SQL语句,以达到执行注入的SQL、进而对web服务器进行攻击的方法。
52b6d379380963b2893355e910642385a5bc01.jpg

网络安全现在越来越受到重视,其中后端应用最常见的安全漏洞就是SQL注入,据owasp(开放式Web应用程序安全项目组织)的安全漏洞统计,注入漏洞常年排名TOP 3。为了尽可能地减少SQL注入问题,我们进行了一些实践总结,并在部门内部进行了推广。

SQL注入原理

SQL注入攻击是通过操作输入来修改SQL语句,以达到执行注入的SQL、进而对web服务器进行攻击的方法。简单的说就是在web表单或页面请求的查询字符串中插入SQL命令, 最终使web服务器执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为http://www.example.com?test=123,此时URL实际向服务器传递了值为123的变量test,这表明当前页面是对数据库进行动态查询的结果。如果服务器使用了SQL动态拼接,此时可以在URL中插入恶意SQL语句并执行。例如预期执行语句是:

select * from user where testId = 123

如果有人恶意构造test=123 or 1 = 1;那么最后实际执行的语句就是:

select * from user where testId = 123 or 1 = 1

SQL注入本质是利用动态SQL拼接并直接执行,并且前端输入的参数未经过严格校验直接参与SQL拼接,输入参数被恶意利用的话就存在不受控的命令注入风险。SQL注入会攻击数据库,所以危害是非常大的,SQL注入的危害包括:1. 数据库信息泄露;2. 数据库恶意操作攻击服务器;3. 删除和修改数据库表信息;4. 服务器远程控制等。防SQL注入原则

根据SQL注入的原理,我们总结了如下防SQL注入的原则。1)尽可能参数化执行SQL,使用PreparedStatement;2)无法参数化执行就对输入参数使用强类型接收,如枚举;3)如果既不能参数化也无法用强类型,就做好格式校验;4)如果上面都做不到,最后考虑做好注入字符的转义过滤。

防SQL注入实践总结

  • 实践1:使用数据库预编译SQL执行

关系型数据库都提供了预编译执行SQL语句的方式,和直接执行有参数拼接的SQL方式不同的是,在SQL预编译阶段,所有参数都会被模板化,参数值不直接参与编译,这样参数值就无法改变SQL的结构,包括语句条数,参数个数,查询条件等等,就算参数里存在恶意注入参数值也无法破坏原来的SQL结构。下面举例说明拼接执行和参数化执行的区别。直接执行:

select name from user_info where name = 'gouge'

使用预编译执行(也就是PreparedStatement)分两步。

第一步:模板化编译

select name from user_info where name = ?

第二步:参数带入

select name from user_info where name = 'gouge'

预编译后的语句执行时,就算参数值里有恶意注入,也只是当成一个字符串的一部分,并不会导致SQL结构被破坏,所以这种方式能够从根本上杜绝SQL注入的风险,也是目前公认防SQL注入最好的办法。

  • 实践2: 可选情况下,优先使用非String类型作为入参

SQL注入依赖于SQL动态拼接,如果非String(包括枚举)参数有强类型严格校验,拼接的时候就没有SQL注入的风险

  • 实践3:对String类型参数进行枚举校验

对于可枚举的String入参,在后端需要进行枚举值白名单校验,防止伪造。以经常使用的排序字段为例,排序字段不少情况下是从前端传过来的,不可信。所以需要做枚举校验,看看传过来的排序字段是否在预期内。

  • 实践4:存储过程

存储过程因为也是预编译的,所以也可以防止SQL注入,不过存储过程已经是不推荐的实践了,这里只是列举一下。

  • 实践5:对String入参进行转义处理

这个实践方案的优先级最低,前面的方案都无法实施或代价太大的情况下,才考虑使用,因为这个办法并不是完全可靠的。这个办法是把用户输入拼到SQL之前,先对输入进行转义,所以实现上是和数据库绑定的,不同的数据库转义实现都不一样。原理是对特定的查询,数据库支持一种或多种转义的方法,如果对所有用户输入的参数拼到SQL之前进行合适的转义,就可以防止SQL注入,但是不能保证所有情况都能处理完美。OWASP组织提供了可以直接使用的java类库esapi,可以用来完成转义,目前支持mysql,oracle,还不支持sqlserver,OWASP官方文档中也有说明这个类库也不能保证100%可以防SQL注入。

Mybatis防SQL注入

Mybatis的XML Mapper中参数有两种表达方式: #{xxx}和${xxx},区别是#{xxx}会被模板化为参数化形式(?),所以参数值不参与编译;${xxx}会被替换为对应的参数值参与编译。所以使用#{xxx}语法的参数没有注入风险,但是使用${xxx}不当,就存在注入风险。我们使用sonar规则来检测${xxx}的使用情况,发现很多可以使用#{xxx}的情况下,却使用了${xxx},大概是使用者还不了解其中的区别和风险。根据防止SQL注入的原则,针对使用Mybatis作为ORM的时候,我们可以得出这样的结论:

  • 在xml中能使用参数化#{}语法实现的,禁止使用${}
  • 不得已必须使用${}语法拼接SQL时,必须先进行合法校验和转义

Mybatis-XML常见误用及安全建议

1)常规误用风险写法:

account = '${account}'

安全写法:

account = #{account}

2) in 参数拼接风险写法:

WHERE id IN (${item.ids})

安全写法:

<if test="ids != null and ids.size() > 0">
                AND id IN
                <foreach
                        collection="ids"
                        item="id"
                        open="("
                        separator=","
                        close=")">
                    #{id}
                </foreach>
            </if>

3) sqlserver top语法风险写法:

top ${queryCnt}

安全写法:

top (#{queryCnt})

4)between语法风险写法:

BETWEEN ${start} AND ${end}

安全写法:

BETWEEN #{start} AND #{end}

5)like语法风险写法:

email like '%${emailSuffix}'

安全写法1:

email like concat('%', #{emailSuffix})

安全写法2:

<bind name="pattern" value="'%'+ emailSuffix">
Email like #{pattern}

安全写法3:在java代码中构造好pattern,使用#{}语法6) limit语法风险写法:

limit ${offset}, ${size}

安全写法:

limit #{offset}, #{size}

7) 动态order by风险写法:

order by ${orderByClause}

安全建议1:可以在xml中使用if代替java中拼凑orderby(推荐);安全建议2:对排序字段和排序类型值进行白名单校验;8)动态表名安全建议:建议实现Mybatis插件,在框架层面对表名进行替换处理,避免在xml使用${}来拼凑表名。9) Mybatis example语法风险用法:

<if test="orderByClause != null">
      order by ${orderByClause}
    </if>

Mybatis生成的example使用,大多数情况下是安全的,但是orderByCluase是通过字符串拼接的,也是不安全的,所以不建议使用。安全建议:可以考虑使用mybatis新出的mybatis-dynamic-sql或者国内开源的mybatis-plus,比example语法更简洁,也更安全。例如:

Query.orderBy(createAt.descending());

WAF除了在SQL语句层面尽量保证防止SQL注入风险之外,基础设施WAF(Web Application Firewall)也是非常有效的防止SQL注入的措施,有条件的也可以考虑部署WAF。

本文主要是记录了经销商技术部在安全方面防止SQL注入的一些实践探索和总结,包括SQL注入的原理,防SQL注入总体遵循的原则,以及具体的实践中的安全建议,希望对其他人也有所帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK