今天在指导一个小型DB2项目开发时,突然想到了为什么SQL注入攻击之类的黑客技术在我接受的DB2的mainframe项目中根本没有提及,应该就是因为DB2的静态SQL语句机制。
我们先来看定义。
静态SQL:在编程语言中,语句以hard code的方式写在程序中,但是其中允许有变量。这样的程序需要经过DB2预编译,将对这样的SQL语句的调用变成native language call。而实际的SQL语句则会被BIND成为DB2系统中的可执行包,与应用程序绑定,被其调用。
典型的静态SQL:
EXEC SQL
UPDATE TESTTABLE SET
COL1 = :VALUE1
COL2 = :VALUE2
WHERE COL3 LIKE :VALUE3
END-EXEC.
其中,COL1, COL2, COL3是TABLE COLUMN名字,而VALUE1, VALUE2, VALUE3是应用程序的NATIVE VARIABLE。
目前,商业DB2应用程序采用的大多是静态SQL,特点是一次BIND,多次稳定运行,省去了数据库每次寻找ACCESS PATH的OVER HEAD,并且,如果环境迁移,只须重新BIND,而不需要重新编译应用程序或该产品配置。
动态SQL:在编程语言中,语句以动态的VARIABLE的方式,提交给DB2运行。每次需要PREPARE STATEMENT FROM A VARIABLE,然后再EXECUTE。最常见的这类应用就是JDBC, ODBC这样的调用。其缺点是,每次运行都需要数据库决定ACCESS PATH,造成了很多OVERHEAD,但是同时如果表中数据信息时常变动,由于每次都做优化处理,有时也会增进性能。
SQL注入攻击是指在提交查询表单时,故意的输入一些SQL保留字,以扰乱程序执行的SQL语句,甚至插入攻击者指定的SQL语句执行。这样的攻击常见于ASP + MS SQL server,但并不局限于此。
比如,某应用提供查询数据库中所有职员姓名信息的功能,要求使用者输入姓名,比如SMITH,便查询出所有姓名为SMITH的职员的员工号。
假设,程序中构造sql的逻辑如下:
SQLSTMT = "SELECT EMP_ID FROM EMP_TABLE WHERE EMP_NAME LIKE /'" +
NAME_FROM_UI + "/'";
EXECUTE SQLSTMT;
则构造出的SQL如下:
SELECT EMP_ID
FROM EMP_TABLE
WHERE EMP_NAME LIKE 'SMITH';
(黄色的字符为从UI接收的输入,下同)
则查询正确;
但是,攻击者如果提交这样的一个姓名:
SMITH'; UPDATE EMP_TABLE SET SALARY = 100000;
如果程序中没有对SQL特殊字符'做转义处理,则生成的SQL就是:
SELECT EMP_ID
FROM EMP_TABLE
WHERE EMP_NAME LIKE 'SMITH'; UPDATE EMP_TABLE SET SALARY = 100000; ';
这样,假设表中真的有SALARY这个COLUMN,并且例子中处理从UI接受到的字串时没有对'进行转义处理,攻击者就能将所有人的薪水改为100000。
(实际上,SQL注入攻击并不是简单转义'就能避免的,攻击者可以直接使用&加'的UNICODE编码来规避程序转义等,在此不深入探讨)
(SQL注入攻击可以有多种方法获取表名、列名,而不仅仅是全凭猜测,最常见的手法为先注入描述表的SQL语句,获取表信息后进行实际攻击等等,在此不再详述,有兴趣的读者可以自己GOOGLE一下。)
以上的攻击,对于动态SQL语句来说,无疑是可以成功的,因为每次执行,数据库都会为这个语句重新确定ACCESS PATH,所以,即使每次语句不同,其执行也不会跑出错误。
而如果是静态SQL语句,由于其ACCESS PATH已经确定,换言之,在上例中就意味着,SELECT语句要使用的索引,优先APPLY过滤条件的顺序之类,都已经确定的写在DB2的系统表中了,这时,如果试图使用SQL注入攻击,DB2已经明确的知道了,要做的查询是什么,并且已经知道了,传进来的变量仅用于过滤条件,这样就杜绝了在查询中注入攻击者的SQL语句的可能。
笔者在毫无输入转义判定的DB2应用程序(静态SQL语句)中测试了例中的注入攻击,结果是DB2正确返回SQLCODE 100,提示找不到需要查询的记录。
由此笔者认为,在使用静态SQL语句的DB2应用程序中,应无SQL注入攻击之虞。