一、SQL注入
SQL注入是一种利用应用程序数据库层出现的安全漏洞的代码注入技术。通常在一些场景容易发生,比如用户输入的内嵌于SQL语句里的转义字符没有被正确的过滤掉或用户输入不是强类型导致异常的执行。SQL注入是注入型漏洞攻击中很常见的一种,注入式漏洞攻击在一种编程语言或脚本语言嵌入到另一种的任何时刻都可能发生。SQL注入是现在常用的应用层注入技术之一。
下图是通过扫描工具:WebCruiser-Web Vulnerability Scanner进行的SQL注入示例:
二、SQL注入的种类
1、没有正确的过滤转义字符
在用户输入转义字符没有被过滤时,就会发生这种形式的注入式攻击。用户的输入会被传递给一个SQL语句,这样恶意用户就能对数据库上的语句实施操纵。下面的这行代码将会演示这种漏洞:
statement = "SELECT * FROM users WHERE name = '" + userName + "';"这段代码用于从用户表中取出一个特定的用户,但如果用户名被一个恶意的用户用一种特定的方式伪造,这个语句所执行的操作可能就不仅仅是代码的作者所期望的那样了。例如,将用户名变量username设置为:
a' or 't'='t对应的原始的SQL语句为:
SELECT * FROM users WHERE name = 'a' OR 't'='t';如果这段代码被用于一个认证过程,那么这个例子就能够强迫系统选择一个合法的用户名,因为赋值 't'='t永远是正确的。
a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't这个输入最终执行的SQL语句为:
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';多数SQL服务器实现允许一次调用执行多条语句,一些SQL APIs比如PHP的mysql_query()出于安全原因不允许此操作。这可以阻止攻击者注入完全独立的查询,而不能阻止他们修改查询。
statement := "SELECT * FROM userinfo WHERE id = " + a_variable + ";"从该语句可以看出,程序设计者希望a_variable是一个与“id”字段有关的数字,然而如果实际传入的是字符串,例如将a_variable设置为:
1;DROP TABLE users将会删除掉users表,因为实际上最终执行的语句是:
SELECT * FROM userinfo WHERE id=1;DROP TABLE users;3、数据库服务器的漏洞
4.1、条件响应
有一种SQL注入迫使数据库在一个普通的应用程序屏幕上计算一个逻辑语句的值:
SELECT booktitle FROM booklist WHERE bookId = 'OOk14cd' AND 1=1;这会显示一个正常的页面,而语句
SELECT booktitle FROM booklist WHERE bookId = 'OOk14cd' AND 1=2;在页面易于受到SQL注入式攻击时,它有可能给出一个不同的结果。
如果WHERE语句为真,这种类型的盲目SQL注入通过强迫数据库计算一个引起错误的语句来引发SQL错误,例如:
SELECT 1/0 FROM users WHERE username='Ralph'显然,如果用户Ralph存在的话,被零除将导致错误。
时间延误是一种盲目SQL注入,根据所注入的逻辑,它可以导致SQL引擎执行一个长队列或一个时间延迟语句。攻击者可以衡量页面加载的时间,从而判定注入的语句是否成功。
二、防御和检查SQL注入的手段
1、使用参数化的过滤性语句
要防御SQL注入,用户的输入就绝对不能直接被嵌入到SQL语句中。恰恰相反,用户的输入必须进行过滤,或者使用参数化的语句。参数化的语句使用参数而不是将用户输入嵌入到语句中。在多数情况中,SQL语句可以被修正。然后,用户输入就被限于一个参数。
下面是一个使用Java和JDBC API的例子:
PreparedStatement prep = conn.prepareStatement("SELECT * FROM USERS WHERE PASSWORD=?"); prep.setString(1, pwd);如何强制使用参数化语句?
从数据库级别强制:目前只有H2数据库支持强制使用参数化查询。
从代码级别强制:使用对象关系映射库避免写SQL代码的需要。ORM库可以从基于对象的代码中生成参数化的SQL语句。当然也可以通过代码复查来检查代码中是否存在非参数化SQL。
2、转码
一种直接的,但易出错的避免注入的方式是转义危险的字符。它易出错的原因之一是,它是一种鲁棒性低于白名单的黑名单。比如,参数中每次出现单引号(')时必须替换为两个单引号(''),以此构成有效的SQL字符串。PHP中,在发送SQL查询之前,常用mysql_real_escape_string函数转码查询参数:
$query = sprintf("SELECT * FROM Users where UserName='%s' and Password='%s'", mysql_real_escape_string($Username), mysql_real_escape_string($Password)); mysql_query($query);对于查询参数需要过滤对数据库造成破坏的字段,比如update,delete,drop等。