报表开发中的 SQL 植入问题

各类应用系统中都会有报表,而大多数报表的数据来源都是关系数据库,也就要用 SQL 来读取数据。
如果用于取数的 SQL 被黑客篡改,就可能造成数据泄漏等安全问题,这就是 SQL 植入或者 SQL 注入问题。

那么报表的取数 SQL 为什么会有机会被篡改呢?

如果这些 SQL 是在后台写死的,那其实是没有什么机会被篡改
但很多报表需要灵活查询,比如这个报表

imagepng
我们希望从很多个维度来查询数据,按时间段查,按部门查,按品类查,按地域查,按其他各种维度查。

imagepng

需要按时间查,SQL 需要写成这样
SELECT … FROM T WHERE DATE>=? AND DATE<=?
还需要按地区,那就得加地区条件,再有其他条件查就得再加上
AND AREA=… AND ***=… AND ***=…
这不仅会导致 SQL 语句异常复杂,而且仍然不够灵活。

聪明的程序员想到了更灵活的方法,把 SQL 写成这样
SELECT … FROM T WHERE ${mac}

查询时根据条件
DATE>=…AND DATE <=…
AND AREA=…
AND XXX=…
AND XXX=…
……
替换掉加粗部分 ${mac} ,每次临时生成一条 SQL 语句,那就可以想怎么查就怎么查了,不必写一条包罗万象的 SQL 了。
灵活是灵活了,但风险也来了,如果把加粗部分 ${mac} 替换成危险的语句,比如这样
SELECT … FROM T WHERE 1=0 UNION SELECT … FROM user

用户信息就泄露了,这就是报表 SQL 植入的风险。

那么,把 SQL 写的复杂一些,让正常的语句可以执行,而植入的危险 SQL 则会变成数据库不识别的错误语句,这不行了吗?
比如给子句加个括号,刚才那种 SQL 植入就挡住了。
SELECT … FROM T WHERE (1=0 UNION SELECT … FROM user)

No,没有这么简单,黑客只需要再加入下面加粗字体的语句就可以骗过括号,还是泄露。
SELECT … FROM T WHERE (1=0 ) UNION SELECT … FROM user) WHERE (1=1)

结果只能搞成这种复杂样子,这不仅麻烦,而且已经严重影响执行效率了。
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

那还能怎么办?
其实很简单,采用敏感词过滤机制。
比如润乾报表,就提供了这个功能。

把 SELECT,FROM 等关键词设置成敏感词,发现传入的宏参数中有这些内容就拒绝掉

imagepng

正常的参数中通常不会有这些关键字,这样就能保证灵活性的同时 1 秒解决问题。
润乾既有灵活 SQL 替换加敏感词过滤的机制,也仍然提供固定 SQL 加固定参数的传统机制应对安全要求更高的场景,灵活和安全两不误。

imagepng

顺便说一句,考虑周全的润乾报表因为采用了互联网营销模式而售价非常低,一万一套,三万买断,妥妥的性价比之王

有些报表工具为了灵活性,也为了图省事,只提供了语句替换的参数方案,却又没有做好相应的安全设置。报表开发人员通常也不会重视 SQL 植入漏洞,结果一旦发生问题,后果就会很严重,选用报表工具时要特别注意这一点。