报表的 SQL 注入风险是什么意思?如何防范?
啥是 SQL 注入风险?
数据库要执行 SQL 访问数据,数据库是个执行机构,它只会检查传来的 SQL 是不是合乎语法,而并不会关心这个语句是否会造成伤害(数据泄露或破坏)。正因为只要符合语法规则就会执行的机制,导致 SQL 有了注入的风险。
SQL 本身就是个字符串,而且一般没有加密,字符串可能被黑客劫持修改,这样就可能造成数据库执行了不该执行的动作。
SQL 注入的惯用做法是通过把 SQL 子串插入到 Web 表单项或页面请求(Url)的查询字符串中提交,最终达到欺骗服务器执行恶意操作的目的。
常见案例包括通过植入 SQL 骗过登录验证。而之前很多影视网站泄露 VIP 会员密码的事件,很多就是通过 SQL 植入到 WEB 表单暴露的,并且这类表单特别容易受到攻击。通过 SQL 植入,不仅可以非法获取账号信息,严重的还能够篡改、删除重要数据信息。
报表的常见数据来源是数据库,尤其关系数据库,执行 SQL 实现取数。
所有的报表工具都会提供参数功能,主要用于用户输入条件后的数据筛选。比如,希望查询指定时间段的数据,可以把时间段作为参数传递给报表,报表在从数据库中取数时将这些参数拼接到取数 SQL 的 WHERE 条件上,就可以根据不同参数取出不同数据来展现了。这种方式要求事先把查询条件做死,也就是固定了对应的条件字段。 比如下面这种传统做法:
SQL:select * from t where date>=? and date<=?
这个 SQL 专用于按时间段查询,如果想用地区查询就搞不了,需要再做一个 area=? 的查询条件或报表,显然非常麻烦……
固定条件不够,还要求更灵活,大多数报表工具又增加了通用查询功能,允许动态拼 SQL 了。比如,SQL 可以写成:
select … from T where ${mac}
其中 ${mac} 就是将来会被参数 mac 动态替换的内容。按时间段查询时,可以把 mac 拼成 date>… and data <=…,按地区查询时则拼成 area=…;当然还可以混合多条件查询拼成 data>… and date<=… and area=…;而无条件时则拼成一个永远为真的条件 1=1。
开了这个口子,报表查询条件确实灵活了,但是一旦被黑客利用,数据就危险了。
例如上面的 mac 拼入“1=0 union all select … from user”,所有的用户信息就可能完全泄露,这也解释了报表为啥有 SQL 注入风险。
总结来说有这么几种方式:
也就是不用动态拼 SQL 方式开发报表,那就没办法注入干坏事儿的 SQL 串了。好处显然是避免了 SQL 注入的风险,所以像开源报表工具就直接不提供灵活条件功能(牺牲掉产品的功能,如果非要搞灵活条件只能去硬编码)。对于提供了灵活条件的产品,简单粗暴地不允许使用这个功能就是了。
找数据库管理牛人,把 SQL 搞成不管参数传来啥内容,拼到报表的 SQL 里都能保证正常条件串仍然可执行,攻击串则非法不可执行。不过这种方式只能说太麻烦、太难(一般人整不了)了,成本忒高(总不能一个报表开发人员还得配个自身 DBA?)。
还是用上面的例子,这个 SQL:select * from T where ${mac},怎么才能防住所有可能的攻击呢?
条件上加上括号,写成
select… from T where (${mac})
的形式。正常的条件串传进来仍然是合法可执行的,而上面那个攻击串传进来之后,SQL 将变成:
select… from T where (1=0 union select … from user)
这是一句不合语法的 SQL,执行会出错,风险似乎就没有了。 且慢,如果黑客把 mac 拼成
1=0) union select … from user where (1=1
整句 SQL 将变成
Select … from T where (1=0) union select … from user where (1=1)
还是一句可执行的合法 SQL,仍然会泄露信息。
原则上,我们要假定最坏情况,要保证黑客即使知道数据库结构和报表 SQL 写法时,仍然无法攻击。我们只能把这个 SQL 写得更复杂一些:
select … from T where (${mac}) or ${mac}
正常的条件串仍然还是合法可执行的,攻击串送进来会变成:
select … from T where (1=0) union select … from user where (1=1) OR 1=0) union select … from user where (1=1
这就非法了,可以挡住这个攻击。
弄成这个样子才可能挡住所有的 SQL 注入攻击,但实际上这个 SQL 已经有点复杂了,而且 SQL 写成这样,执行效率也会受到影响,条件有时候会被执行两次(当 w 为假时,第二遍 w 会没必要地再计算一次)。但为了安全性,却没有什么好办法。 这个例子说明,想挡住 SQL 注入攻击并不是非常轻松的事情,更不是一般的报表开发人员(相当多对安全意识不够强)可以做到的。
只需要对参数值(SQL 子串)进行检测,当包含某些特定词(或敏感词)的时候直接拒绝继续运算报表,比如很少会用 select,from、union 等这些关键字作为库表字段名,只要判断条件串中有这些词,就认为是攻击并中断报表执行。
当然了,SQL 的条件串里正常情况下也可能会有这些关键字,但是相对较少,为了更好的安全性,这点灵活性的损失还是可以理解的。
这种方式,商业报表开发工具一般都会提供(上图敏感词配置为润乾报表实现方式),也不排除有些报表工具只提供拼 SQL 实现灵活条件的方案,但没考虑规避 SQL 注入风险,是很危险的。
总结来说,只要报表用到 SQL 的灵活条件查询功能,就存在注入风险,完全放弃此功能有点一刀切的意思,好的功能还是要发挥其作用的,我们更应该尽可能做好防范。