王保胜, 杨新锋
(南阳理工学院, 南阳 473000)
随着研究用脚本语言攻击站点的人数的增加,SQL Injection攻击也倍受关注,攻击者利用SQL Injection漏洞可以在几分钟内,甚至几十秒时间内,入侵至目标服务器,获取未授权权限,窃取不公开信息,修改相关信息,甚至删除数据,几十秒钟的时间足够在数据库里面注入数以万计的非法数据[1-2]。造成这些后果的主要原因就是程序员在编程的失误或者粗心,然而这些问题完全是可以避免的。国外对于SQL Injection的研究已经相当靠前,由于计算机在中国国内的发展时间比较短,国内对SQL Injection攻击这方面的研究比较少[3-4]。
SQL Injection是攻击者把影响程序正常反应的SQL命令添加到Web表单的输入区域,或者是页面请求执行的字符命令中,而达到对服务器的欺骗,从而执行这个影响程序正常反应的SQL字符命令的攻击方式[5-6]。
SQL Injection攻击网站一般都是通过80端口,通过欺骗等手段提交恶意代码[7]。通过人眼根本无法分辨,甚至通过软件也无法测试出来,因为SQL Injection访问网站的方法与正常访问者的访问方式并无不同之处,只有管理员查看web日志的时候,才能捕捉一些攻击信息。
如果软件或者网站存在SQL Injection漏洞,攻击者就能够对数据库里面的数据进行非法操作,在非法得到一定的服务器权限后,攻击者甚至可以在服务器中通过欺骗的手段添加木马,安全性较低的数据库甚至可能被得到服务器的管理员权限,这就说明一个数据库的不安全,有可能导致整个站点的内容都受到攻击的威胁。
SQL Injection的本质是攻击者把影响程序正常反应的SQL命令添加到Web表单的输入区域,或者是页面请求执行的字符命令中,而达到对服务器的欺骗,从而执行这个影响程序正常反应的SQL字符命令,并最终将执行结果返回给攻击者[8-9]。
SQL Injection流程,如图1所示。
图1 SQL Injection流程
(1)判断Web系统脚本语言是使用了哪种计算机语言,找到注入点,确定是否含有SQL Injection漏洞。
(2)判断Web系统的数据库类型。
(3)判断一个数据库中表和相应字段的结构。
(4)构造sql Injection语句,从而得到表中数据内容。
(5)查找网站管理的后台,使用得到的管理员账号及密码登录后台。
(6)结合站点其他漏洞,上传一个Webshell。
(7)对用户进一步提权,从而得到服务器的系统权限,队整个站点进行非法操作。
(8)删除侵入痕迹,本地保留侵入方法,以待下一次侵入使用。
下面以管理者和程序员的身份,从防御的角度来谈一下如何防止SQL Injection。
在编写PHP代码的时候,要充分考虑用户输入信息的安全性。
2.1.1 初始化变量
编写程序的时候一定要初始化函数里面的变量,代码如下:
if ($admin){
echo‘恭喜你,登陆成功!’;
include(‘admin.php’);
}else{
echo‘你不是管理员,请停止你的非法操作!’;
}
?>
假设后台登录的这个页面的地址是http://127.0.0.1/login.php,那么在浏览器的地址栏里面提交:http://127.0.0.1/login.php?admin =1,显而易见,问题现了,每个人都是管理员了,只要是进行这个操作的浏览者,都可以使用管理员权限了。
或者将配置文件里面设置register_global =off这样就能巧妙地那么就能避免了。重新设计代码:
设计代码实现后,再重新在地址栏中输入提交原来的地址:http://127.0.0.1/login.php?admin=1,就会发现,已经不能进去了。原因就是在代码中对变量“$admin”进行了初始化了,令$admin = 0,现在访问者就无法通过这个漏洞获取管理员权限,也没有办法进入系统了。由此可以看到,变量的初始化是多么的重要了,变量的初始化,能够减少漏洞的产生。
$admin=0; //初始化变量
if ($_POST[‘admin_user’]&& $_POST[‘admin_pass’]){
//假如用户名和密码没有进过处理则不运行代码
//……
$admin=1;
}else{
$admin=0;
}
if($admin){
echo‘恭喜你,登陆成功!’;
include(‘admin.php’);
}else{
echo‘你不是管理员,请停止你的非法操作!’;
}
?>
2.1.2 过滤变量
提交数据的方式有两种,一种是用get,另一种是用post提交。而很多种SQL Injection都是从get提交方式上面着手,寻找利用这方面的漏洞。这些传值一定会利用SQL语句,打破原来的SQL语句,构造新的SQL语句。SQL语句的四大句无外乎select、update、delete和insert,SQL Injection就是利用这4种语句构造输入语句的。可以利用如下代码发现和避免这些问题:
fuction inject_($sql_str){
return eregi(‘select|insert|update|delete’||
function verify_id($id=null){
//是否为空判断
if($id){exit(‘没有提交参数!’):}
//注入判断
else if(inject_check($id)){exit(‘提交的参数非法!’);}
//数字判断
else if(!is_numeric($id)){exit(‘提交的参数非法!’);}
$id=intval($id);//整型化
Returm $id;
}
?>
这样就能够进行校验了,上面的程序代码可以进一步改写成:
if (inject_check($_GET[‘id’])){ exit(‘你提交的信息不是正常信息,请检重新提交合法信息!’);
}else{
//这里调用了一个过滤的函数;对$id的值进行过滤防止非法数值的传入
$id=verifty_id($_GET[‘id’]));
echo‘你提交的信息不是正常信息,进行下一步操作!’;
}
?>
如此设计代码,问题似乎都已经被解决了。Get传值的类型解决了,就要考虑与它不同的post传值的类型了,当然少不了要考虑大批量一起传输数据。
用户在提交的数据中可能会有对数据库造成影响的字符,比如'_','%'这些字符都具有特殊的意义,那就应该对这些字符进行处理。
在PHP配置文件里面设置magic_quotes_gpc = off,用户提交的不符合数据库规则的数据,都不会自动的在前面加' '的,这样就避免了被注入的可能,代码实现如下:
function str_check($str){
//判断magic_quotes_gpc是否打开
if(!get_magic_quotes_gpc()){
$str=addslashes($str);//进行过滤
}
$str=str_replace("_","\_",$str);//把‘_’过滤掉
$str=str_replace("%","\%",$str);//把‘%’过滤掉
return $str;
}
?>
然而,在大批量的数据面前应该怎么考虑呢?只需要将数据进行HTML标记转换,代码实现:
2.2.1 登录注册页面的安全防范
通注册页面采用短信验证或者邮箱验证,防止水军注水,缓解服务器压力,注册者使用验证码,或者使用邮件里面的链接完成对账号的注册功能。
注册的时候,对输入的账号进行正则判断,调用的正则方程式,而且跟数据里的已有账号作对比,已存在的账号,不能继续注册。注册的时候前台用js代码进行判断,目的是减轻服务器的压力,同时,PHP代码也是需要判断,后台代码进行判断的目的是防止攻击者屏蔽js脚本,非法传入数据对
function post_check($post){
//判断magic_quotes_gpc是否为打开
if(!get_magic_quotes_gpc()){
//进行magic_quotes_gpc没有打开的情况对提交数据的过滤
$post=addslashes($post);
}
$post=str_replace("_","\_",$post);//把‘_’过滤掉
$post=str_replace("%","\%",$post);//把‘%’过滤掉
$post=htmlspecialchars($post);//html标记转换
return $post;
}
?>
服务器的安全产生威胁。注册和登录的时候,对输入的密码进行HTML标记转换,然后把它的MD5值写入数据库,或者和数据库里面的值作对比。代码如下:
$data['username']= $wget['username'];
$wget['password']=htmlspecialchars ($wget['password']);
$data['password']= md5($wget['password']);
邮箱注册注册完了,需要验证,数据库对邮箱注册的账号有一个拍段,没有进过验证的账号,是不能够登录的。
2.2.2 连接数据库
很过PHP程序员使用@来抑制程序的报错,代码如下所示:
$con=@mysql_connect("localhost","peter","abc123";
if(!$con){
die('Could not connect:'.mysql_error()};
}
这种做法不科学,不能真正意义上解决安全问题,登录数据库可以采用thinkPHP框架的连接数据库的方式,其核心代码如下:
'DB_TYPE'⟹'mysql',//数据库数据
'DB_HOST'⟹'127.0.0.1',//服务器地址
'DB_NAME'⟹'lagou',//数据库名
'DB_USER'⟹'root',//用户名
'DB_PWD'⟹'',//密码
'DB_PORT'⟹'3306',//端口
'DB_PREFIX'⟹'lg_',//数据库表前缀
'DB_CHARSET'⟹'utf8',//字符集
'DB_DEBUG'⟹TRUE,//数据库调试模式开启后可以记录SQL日志3.2.3新增
2.2.3 变量及危险函数的处理
对于每一个变量,尤其是危险函数的变量,都可以用“‘’”引起来,然后对变量进行HTML标记转换,这样就限制住了攻击者的非法输入,将用户输入的“”,“’”等转换成字符,这样就避免了SQL漏洞的产生。使用htmlspecialchars()函数将这些危险符号转换成为字符,不再具有其符号意义。例如登录时过滤的代码:
$data['username']= $wget['username'];
$wget['password']=htmlspecialchars ($wget['password']);
$data['password']= md5($wget['password']);
每一个函数的变量都进行初始化,并且传值时都进过过滤,保证传进来的都是安全的数据。
正确配置php.ini文件,才能够确保脚本运行环境和资源的安全性。
可以使用任何有编辑功能的工具打开“php.ini”文件,对这个文件里面的代码进行更改,但是要注意的是有些东西更改后会造成数据库的不安全的,不要对这个文件修改的太多,只修改有助于提高数据库安全性的地方就可以了,如图2所示。
(1)打开PHP配置中的安全模式:
safe_mode = on
(2)用户组的安全:safe_mode_gid = off
(3)安全模式下执行程序主目录:safe_mode_exec_dir推荐不要去执行任何一个程序。
(4)安全模式下的包含文件:
safe_mode_include_dir
(5)控制php脚本能访问的目录,使用open_basedir选项,这样就能够控制PHP脚本,让访问者使其只能访问它所指定的目录。
(6)关闭危险函数:disable_functions = system,passthru,exec,shell_exec,popen,phpinfo
(7)关闭PHP版本信息:
expose_php = Off
(8)关闭注册全局变量选项:
register_globals = Off
(9)打开“magic_quotes_gpc”:
magic_quotes_gpc = On
(10)错误信息的控制:
display_errors = Off
(11)错误日志:log_errors = On
本文对基于PHP的SQL Injection进行了一定程度上的研究和总结,对于整个数据库安全问题的研究,在各个方面都有所讨论。但是,SQL Injection不仅仅出现在PHP中,计算机技术的迅速发展,难保系统不会受到更严峻的挑战。本文提出thinkPHP框架的使用可以避免很多出现SQL漏洞的可能性,只需要考虑少量的问题就能够避免SQL Injection的问题,但是不是所有的PHP开发人员都会使用框架开发,那些面向过程开发的程序员面临的问题更加多,他们需要处理的问题也更加多,这方面还需要继续研究。另外一个问题就是对于系统的复杂度是否也对这些解决办法有所影响,也有待去完善和继续探究。
[1] 方自远.SQL注入攻击及其检测防御技术研究[J].智能计算机与应用,2016,6(6):87-89.
[2] 张志超,王丹,赵文兵,等.一种基于神经网络的SQL注入漏洞的检测模型[J].计算机与现代化,2016(10):67-71.
[3] 李虎军,林学华,张辽宁.SQL注入攻击与防护探析[J].安徽电子信息职业技术学院学报,2016,15(2):58-63.
[4] 李俊锋.基于Apache+PHP+Mysql网站SQL注入防护探讨[J].网络空间安全,2016(11):93-95.
[5] 韩宸望,林晖,黄川.基于SQL语法树的SQL注入过滤方法研究[J].网络与信息安全学报,2016,2(11):00113.1-8.
[6] 张慧琳,丁羽,张利华,等.基于敏感字符的SQL注入攻击防御方法[J].计算机研究与发展,2016,53(10):2262-2276.
[7] 王苗苗,钱步仁,许莹莹,等.基于通用规则的SQL注入攻击检测与防御系统的研究[J].电子设计工程,2017,25(5):24-29.
[8] 仇善梁.开发者视角下网站SQL注入漏洞及防范[J].河北软件职业技术学院学报,2016,18(4):52-55.
[9] 赵阳,郭玉翠.新型SQL注入攻击的研究与防范[J].计算机系统应用,2016,25(6):52-55.