SQL注入式攻击

       所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。

一、SQL注入攻击的种类

首先要了解注入方式有哪些种类,才能更好的防御注入。

1. 没有正确的过滤转义字符

用户的输入没有进行转义字符过滤,而被传递给SQL语句,从而导致攻击者对数据库的操作,例如:

SELECT * FROM users WHERE name = ?

如果用户名name被恶意伪造为

zhang3' or '1'=1

此时SQL语句就会发生变化:

SELECT * FROM users WHERE name = 'zhang3' OR '1'='1';

因为'1'='1'是正确的,如果这段代码被用语一个认证过程,那么就能强迫选择一个合法的用户名。

2. 错误类型

如果程序没有实施类型强制,就会发生这种形式的攻击,例如:

UPDATE subject SET score= ? where name = 'zhang3' 

如果成绩被恶意伪造为

100; DROP TABLE users #

此时SQL语句就会发生变化

UPDATE subject SET score= 100; DROP TABLE users; # where name = 'zhang3'

它会将“users”表从数据库中删除

3. 防注入函数中的漏洞

有时,一些防注入的过滤函数中也存在着漏洞,如PHP中的addslash,以及MYSQL服务器中mysql_real_escape_string()函数漏洞。这种漏洞允许一个攻击者根据错误的统一字符编码执行一次成功的SQL注入式攻击,例如:

输入$name=41�' or sleep(10.10)=0 limit 1#

当使用addslashes($name)

//SQL输出可能为: 

"SELECT COUNT(*) AS total FROM users WHERE `name` LIKE 'A¿\\\' or sleep(10.10)=0 limit 1#%';"

其原因是addslash对于字符�'的漏洞。该漏洞最早2006年被国外用来讨论数据库字符集设为GBK时,0xbf27本身不是一个有效的GBK字符,但经过  addslashes()  转换后变为0xbf5c27,前面的0xbf5c是个有效的GBK字符,所以0xbf5c27会被当作一个字符0xbf5c和一个单引号来处理,结果漏洞就触发了。

网络上查询到有人说,当mysql_real_escape_string检测到的编码方式跟client设置的编码方式(big5/bgk)不一致时,mysql_real_escape_string跟addslashes是没有区别的

4. 盲目SQL注入式攻击

当一个Web应用程序容易被攻击,而其结果对攻击者却不可见时,就会发生所谓的盲目SQL注入式攻击。有漏洞的网页可能并不会显示数据,而是根据注入到合法语句中的逻辑语句的结果显示不同的内容。这种攻击相当耗时,需要攻击者根据间接数据去推断,但是一旦漏洞的位置和目标信息的位置被确立以后,一种称为Absinthe的工具就可以使这种攻击自动化

5.条件响应

有一种SQL注入能够迫使数据库在一个普通的应用程序上计算一个逻辑语句的值,例如:

SELECT title FROM book WHERE bookId = '234' AND 1=1

这会产生一个标准的输出,而语句

SELECT title FROM book WHERE bookId = '234' AND 1=2在页面容易被SQL注入式攻击时,它有可能给出一个不同的结果,根据二者关系来判断程序错误处理逻辑。

6.条件性差错

条件性差错是一种盲目的SQL注入,如果WHERE语句为真,SQL注入会迫使数据库执行一个引起错误的语句,从而导致一个SQL错误。例如:

SELECT 1/0 FROM users WHERE rname='zhang3'

显然,如果用户zhang3存在的话,被零除将导致错误。

7.时间延误

时间延误是一种盲目的SQL注入,根据所注入的逻辑,它可以导致SQL引擎执行一个长队列或者是一个时间延误语句。攻击者通过衡量页面加载的时间,可以判定所注入的语句是否执行。

二、SQL注入的主要形式

1. 直接攻击,将代码插入到与 SQL 命令中,并使用户输入的注入代码得意执行。

例如像表中插入一条数据,

INSERT INTO tbl_name(col1,col2,col3,col4)  VALUES (xxx,xxxx,$_GET[id],xxx);

Mysql  支持在 value 中使用 select 子查询

INSERT INTO tbl_name (col1,col2,col3,col4) VALUES (xxx, xxxx, (SELECT * FROM tbl_other), xxx);

2. 间接攻击,会将恶意代码注入到表中存储或作为元数据存储的字符串。在存储的字符串中会连接到到一个动态 SQL 命令中,以执行该恶意代码。

例如在已经过滤输入内容后,也可能产生二次攻击

Username: admin'-- 

Password: password

程序正确的过滤了单引号,'insert'语句如下:

INSERT INTO users VALUES ('admin''--', 'password')

假设程序允许用户更改密码,用户名为admin'--,上面的查询就变成了这样: 

update users set password = 'password' where username = 'admin'--'

因此攻击者通过注册一个admin'--用户修改了admin的密码

三、如何防范SQL注入

1. 验证用户输入内容,使恶意参数不能直接被嵌入到SQL语句中,但是这种方法往往不能够考虑的很全面。

2. 使用参数化的过滤性语句,参数化的语句使用参数不是将用户输入直接嵌入到SQL语句中。用户输入就被限于一个参数。

3. 避免使用解释程序,因为这是攻击者借以执行非法命令的手段。

4. 精心编制错误消息,攻击者可以利用错误信息来获取数据库信息;

5. 确保数据库安全,使用一些额外的方式强化数据库安全。首先锁定应用数据,采用最小权限登陆数据库,撤销不必要的公共许可,使用强大的加密技术来保护敏感数据并维护审查跟踪。其次锁定数据库,对额外的系统对象锁定,约束即席查询,加强对验证周边严格控制,最低权限操作系统账户并确保数据库打了最新补丁。

6. 实施代码的安全检查。首先,要在部署应用之前实施安全测试,还应在部署之后用漏洞扫描工具和站点监视工具对网站进行测试。

四、PHP处理SQL注入方法

处理SQL注入的方法很多,先总结以下两种方案:

1. 使用传统方法来避免SQL注入风险

采用输入验证并过滤输入数据的方式,网上有很多诸如此类的方法,下面是程序员小辉在一片文章中提到过的方法,是一个用来获取安全参数值的方法,为了防止用户的错误数据和 php + mysql 注入。

define("XH_PARAM_INT",0);

define("XH_PARAM_TXT",1);

function PAPI_GetSafeParam($pi_strName, $pi_Def = "", $pi_iType = XH_PARAM_TXT)

{

  if ( isset($_GET[$pi_strName]) ) 

    $t_Val = trim($_GET[$pi_strName]);

  else if ( isset($_POST[$pi_strName]))

    $t_Val = trim($_POST[$pi_strName]);

  else 

    return $pi_Def;

  // INT

  if ( XH_PARAM_INT == $pi_iType)

  {

    if (is_numeric($t_Val))

      return $t_Val;

    else

      return $pi_Def;

  }

  

  // String

  $t_Val = str_replace("&", "&",$t_Val); 

  $t_Val = str_replace("<", "<",$t_Val);

  $t_Val = str_replace(">", ">",$t_Val);

  if ( get_magic_quotes_gpc() )

  {

    $t_Val = str_replace("\\\"", """,$t_Val);

    $t_Val = str_replace("\\''", "'",$t_Val);

  }

  else

  {

    $t_Val = str_replace("\"", """,$t_Val);

    $t_Val = str_replace("'", "'",$t_Val);

  }

  return $t_Val;

}

2. 使用PDO查询Mysql来避免SQL注入风险

当使用传统的mysql_connect、mysql_query方法来连接查询数据库时,如果过滤考虑的不够全面,就有SQL注入的防线,导致网站被攻击。虽然mysql_real_escape_string()函数能过滤用户提交的值,但是也有0xbf27转换0xbf5c27的漏洞,而使用PDO的prepare方法来组合sql语句,避免直接使用用户提交的值来作为查询条件,从而可以降低SQL注入的风险。

下面我们看一段代码使用实例:

$dbh = new PDO("mysql:host=localhost; dbname=demo", "user", "pass");

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 

$dbh->exec("set names 'utf8'"); 

$sql="select * from user where name = ? and password = ?";

$stm = $dbh->prepare($sql); 

$exec= $stm->execute(array($name, $password)); 

if ($exec) { 

$row = $stm->fetch(PDO::FETCH_ASSOC);

}

$dbh = null;

在使用PDO访问mysql数据库时,真正的real prepared statements默认情况下是不使用的,setAttribute这一行强制性的禁用PDO模拟预处理语句,这样可以确保SQL语句和相应的值在传递到mysql服务器之前是不会被PHP解析的。

当调用prepare()时,查询语句已经发送给了数据库服务器,此时值只有占位符?发送到数据库服务器,当调用execute()时,用户提交的数据才会传递给数据库,它们是分开传送的,两者独立,可以避免SQL注入。

但是PDO并不能帮助完全防范SQL注入,例如:

1. 不能让占位符?代替一组值(select * from user where uid in (123, 321,445))

2. 不能让占位符?代替表明或列名(select * from user order by score)

3. 不能让占位符?代替其他SQL语法(select extract(year from orderdata) as orderyear from orders where orderid=1)

转自:http://blog.sina.com.cn/s/blog_616acf520101aitl.html

猜你喜欢

转载自chriszeng87.iteye.com/blog/1921708