您的位置:首页 > 数据库

[web安全]SQL注入防御方法总结

2015-05-06 13:34 525 查看
SQL注入概念:程序对于用户的输入未作处理就直接放到SQL语句中执行,导致用户输入的特殊字符可以改变语句的原有逻辑,结果可执行任意的SQL语句。
防御主要分为两种:数据类型判断(针对数字型注入)和特殊字符转义(针对字符型注入)

1.escape处理

一般会用到两个函数mysql_real_escape_string()addslashes()

mysql_real_escape_string()函数会转义:' " \r \n NULL Control-Z

addslashes()函数会转义:' " 反斜线和NULL
这种方式对付通过SQL注入绕过登录认证,即所谓的万能密码,特别有效。也可用来对付字符型注入,因为攻击者需要注入单引号来达到引号平衡。

但是,此处需要注意“二次注入“的问题

以PHP为例,当开启了magic_quotes_gpc之后,将会对特殊字符转义,比如,将'转义为\'

SQL语句:insert into userinfo(id,username,password) values (1,'$username','$password')

通过网站插入数据:id为1、username为admin' -- 、password为123456,那么SQL语句如下:

insert into userinfo(id,username,password) values (1,'admin\' -- ','123456')

但是secbug\'在插入数据库后却没有“\”

+----+------------+-----------+

| id |  username  |  password |

+----+------------+-----------+

|  1 | admin' --  |   123456  |

+----+------------+-----------+

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

update users set password = '[new_password]' where username = 'admin' -- '

因此攻击者通过注册一个admin' --用户修改了admin的密码
为了预防SQL注入攻击,程序将用户输入进行了转义,但是这些数据却又在“未被转义”的查询窗体中重复使用。

2.使用预编译语句

一般来说,防御SQL注入的最佳方式,就是使用预编译语句,绑定变量。
使用预编译的SQL语句,SQL语句的语义不会发生改变,在SQL语句中,变量用?表示,攻击者无法改变SQL的结构,即使攻击者输入试图改变SQL语句结构的输入,程序也仅会将其当作普通字符串来处理。

Java例子:

int id = Integer.parseInt(request.getParameter("id"));

String sql = "select id, username, password from users where id=?";

PreparedStatement ps = this.conn.prepareStatement(sql);

ps.setInt(1,id);

ResultSet res = ps.executeQuery();

Users user = new Users();

if(res.next()){
//封装user对象属性

}

3.使用存储过程

存储过程是在大型数据库系统中,一组为了完成特定功能或经常使用的SQL语句集。

存储过程可避免SQL注入,但也可能会存在注入问题,因此应该尽量避免在存储过程中使用动态的SQL语句。如果无法避免,则应该使用严格的输入过滤或者是编码函数来处理用户的输入数据。

CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");

cs.setString(1,custname);
其中sp_getAccountBalance是预先在数据库中定义好的存储过程。

示例:存储过程如果编写不当,依然有SQL注入的风险

   create proc findUserid @id varchar(100)

as

   exec('select * from Student where StudentNo='+@id);

go

传入参数3 or 1=1将查询出全部数据,改进代码如下:

   create proc findUserid @id varchar(100)

as

   select * from Student where StudentNo=@id

go

当再次传入参数3 or 1=1时,SQL执行器会抛出错误:

消息245,级别16,状态1,过程findUserid,第3行

在将varchar值'3 or 1=1' 转换成数据类型int时失败

4.检查数据类型

检查输入数据的数据类型,在很大程度上可以对抗SQL注入。

settype($offset,'integer');

$query="SELECT id,name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";

或者
$query=sprintf("SELECT id,name FROM products ORDER BY name LIMIT 20 OFFSET %d;",$offset);

还有:is_numeric() , ctype_digit()

5.使用安全函数

OWASP ESAPI中的实现:

ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam);

在使用时,

Codec ORACLE_CODEC = new OracleCodec();

String query = "SELECT user_id FROM user_data WHERE user_name = '" + ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("userID")) + "' and user_password = '" +ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("pwd"))
+ "'";
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: