SQL注入是我们在程序开发过程中经常要注意的问题,属于发生于应用程序之数据库层的安全漏洞,通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了检查,那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏。
这是一个简单的数据表:
CREATE TABLE `user` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
`age` smallint(3) NOT NULL,
`is_admin` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES (‘1′, ‘tom’, ‘2313sdf’, ’10’, ‘0’);
INSERT INTO `user` VALUES (‘2′, ‘lucy’, ‘sdff234′, ‘5’, ‘0’);
INSERT INTO `user` VALUES (‘3′, ‘teacher wang’, ‘salfdjlkvjaldf’, ’24’, ‘1’);
请看下面这些sql语句:
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
情况一:
若用户通过表单提交的信息是:
$name = “tom’ or ‘1’=’1″;
$password = “test”;
数据在未经检验处理的情况下,导致原本的SQL字符串被解析为:
SELECT * from user WHERE name=’tom’ or ‘1’=’1′ and password=’test';
sql执行结果为:
我们在没有输出正确密码的情况下,拿到了“tom”的信息,即用“tom”的身份登录了网站。
情况二:
若用户通过表单提交的信息是:
$name = “‘ or name!=” and is_admin=1 or ‘1’=’1″;
$password = “test”;
数据在未经检验处理的情况下,导致原本的SQL字符串被解析为:
SELECT * from user WHERE name=” or name!=” and is_admin=1 or ‘1’=’1′ and password=’test';
sql执行结果为:
我们在没有输出正确用户名及密码的情况下,拿到了“teacher wang”的信息,即用“teacher wang”的身份登录了网站。
情况三:
若用户通过表单提交的信息是:
$name = “‘ ; DELETE FROM user;'”;
$password = “test”;
数据在未经检验处理的情况下,导致原本的SQL字符串被解析为:
SELECT * from user WHERE name=” ; DELETE FROM user;” and password=’test';
sql执行结果为:
悲剧了,user表数据被清空了
如何防范SQL注入攻击:
- 在组合SQL字符串时,先针对所传入的参数作字符转义
- 如果使用PHP开发网页程序的话,亦可打开PHP的Magic quote功能自动将所有的网页传入参数,将单引号字符取代为连续2个单引号字符。
- 如果可能应该过滤以下字符:分号“;”,两个减号“–”,单引号“’”,注释“/* … */”。
- 更换危险字符。例如在PHP通过addslashes()函数保护SQL注入。
- 限制用户输入的长度,限制用户输入的取值范围。
- 为当前应用建立权限比较小的数据库用户,这样不会导致数据库管理员丢失。
php代码实现:
function mysql_prepare_for_request($value, $type = “string”) {
$return = $value;
switch ($type) {
case “string” :
// 去除斜杠
if (get_magic_quotes_gpc ()) {
$return = stripslashes ( $return );
}
//配置连接有效数据库
$con = mysql_connect ( ‘localhost’, ‘root’, ‘root’ );//填写正确的用户名,密码
if (! $con) {
die ( ‘Could not connect: ‘ . mysql_error () );
}
$return = mysql_real_escape_string ( $return );
mysql_close ( $con );
break;
case “number” :
if (! is_numeric ( $return )) {
$return = 0;
}
break;
default :
$return = “”;
break;
}
return $return;
}
//情况一:用户输入
$name = “tom’ or ‘1’=’1″;
$password = “test”;
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
echo “危险:”.$sql.”<br/>”;
//数据转义过滤
$name = mysql_prepare_for_request ( $name, ‘string’ );
$password = mysql_prepare_for_request ( $password, ‘string’ );
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
echo “安全:”.$sql.”<br/><br/>”;
//情况二:用户输入
$name = “‘ or name!=” and is_admin=1 or ‘1’=’1″;
$password = “test”;
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
echo “危险:”.$sql.”<br/>”;
//数据转义过滤
$name = mysql_prepare_for_request ( $name, ‘string’ );
$password = mysql_prepare_for_request ( $password, ‘string’ );
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
echo “安全:”.$sql.”<br/><br/>”;
//情况三:用户输入
$name = “‘ ; DELETE FROM user;'”;
$password = “test”;
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
echo “危险:”.$sql.”<br/>”;
//数据转义过滤
$name = mysql_prepare_for_request ( $name, ‘string’ );
$password = mysql_prepare_for_request ( $password, ‘string’ );
$sql=”SELECT * from user WHERE name=’$name’ and password=’$password';”;
echo “安全:”.$sql.”<br/><br/>”;
输出结果:
危险:SELECT * from user WHERE name=’小明’ or ‘1’=’1′ and password=’test';
安全:SELECT * from user WHERE name=’小明\’ or \’1\’=\’1′ and password=’test';
危险:SELECT * from user WHERE name=” or name!=” and is_admin=1 or ‘1’=’1′ and password=’test';
安全:SELECT * from user WHERE name=’\’ or name!=\’\’ and is_admin=1 or \’1\’=\’1′ and password=’test';
危险:SELECT * from user WHERE name=” ; DELETE FROM user;” and password=’test';
安全:SELECT * from user WHERE name=’\’ ; DELETE FROM user;\” and password=’test';