监控安装 ERP

系统集成论坛

 找回密码
 注册通行证

QQ登录

只需一步,快速开始

路由器交换机防火墙系统集成商城 优质产品采购平台
查看: 2131|回复: 1
打印 上一主题 下一主题

PHP防注入

[复制链接]
跳转到指定楼层
1
发表于 2011-3-20 21:34:40 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
华为金牌代理
php网站如何防止sql注入?

网站的运行安全肯定是每个站长必须考虑的问题,大家知道,大多数黑客攻击网站都是采用sql注入,这就是我们常说的为什么最原始的静态的网站反而是最安全的。 今天我们讲讲PHP注入的安全规范,防止自己的网站被sql注入。

如今主流的网站开发语言还是php,那我们就从php网站如何防止sql注入开始说起:

Php注入的安全防范通过上面的过程,我们可以了解到php注入的原理和手法,当然我们也同样可以制定出相应该的防范方法:
首先是对服务器的安全设置,这里主要是php+mysql的安全设置和linux主机的安全设置。对php+mysql注射的防范,首先将magic_quotes_gpc设置为On,display_errors设置为Off,如果id型,我们利用intval()将其转换成整数类型,如代码:
$id=intval($id);
mysql_query=”select *from example where articieid=’$id’”;或者这样写:mysql_query(”SELECT * FROM article WHERE articleid=”.intval($id).”")

如果是字符型就用addslashes()过滤一下,然后再过滤”%”和”_”如:
$search=addslashes($search);
$search=str_replace(“_”,”\_”,$search);
$search=str_replace(“%”,”\%”,$search);
当然也可以加php通用防注入代码:
/*************************
PHP通用防注入安全代码
说明:
判断传递的变量中是否含有非法字符
如$_POST、$_GET
功能:
防注入
**************************/
//要过滤的非法字符
$ArrFiltrate=array(”‘”,”;”,”union”);
//出错后要跳转的url,不填则默认前一页
$StrGoUrl=”";
//是否存在数组中的值
function FunStringExist($StrFiltrate,$ArrFiltrate){
foreach ($ArrFiltrate as $key=>$value){
if (eregi($value,$StrFiltrate)){
return true;
}
}
return false;
}
//合并$_POST 和 $_GET
if(function_exists(array_merge)){
$ArrPostAndGet=array_merge($HTTP_POST_VARS,$HTTP_GET_VARS);
}else{
foreach($HTTP_POST_VARS as $key=>$value){
$ArrPostAndGet[]=$value;
}
foreach($HTTP_GET_VARS as $key=>$value){
$ArrPostAndGet[]=$value;
}
}
//验证开始
foreach($ArrPostAndGet as $key=>$value){
if (FunStringExist($value,$ArrFiltrate)){
echo “alert(/”Neeao提示,非法字符/”);”;
if (empty($StrGoUrl)){
echo “history.go(-1);”;
}else{
echo “window.location=/”".$StrGoUrl.”/”;”;
}
exit;
}
}
?>
/*************************
保存为checkpostandget.php
然后在每个php文件前加include(“checkpostandget.php“);即可
**************************/

另外将管理员用户名和密码都采取md5加密,这样就能有效地防止了php的注入。
还有服务器和mysql也要加强一些安全防范。
对于linux服务器的安全设置:
加密口令,使用“/usr/sbin/authconfig”工具打开密码的shadow功能,对password进行加密。
禁止访问重要文件,进入linux命令界面,在提示符下输入:
#chmod 600 /etc/inetd.conf //改变文件属性为600
#chattr +I   /etc/inetd.conf     //保证文件属主为root
#chattr –I   /etc/inetd.conf     // 对该文件的改变做限制
禁止任何用户通过su命令改变为root用户
在su配置文件即/etc/pam.d/目录下的开头添加下面两行:
Auth   sufficient   /lib/security/pam_rootok.so debug
Auth   required   /lib/security/pam_whell.so group=wheel
删除所有的特殊帐户
#userdel   lp等等 删除用户
#groupdel lp等等 删除组
禁止不使用的suid/sgid程序
#find / -type f \(-perm -04000   - o –perm -02000 \) \-execls –lg {} \;



+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



一直搞.net ,最近研究PHP,也涉及到防注入的安全措施,和.net的大同小异,从网上摘回一篇,作为备注,以供自己需要时查阅。
介绍两种方法吧,首先请把以下代码保存为safe.php放在网站根目录下,然后在每个php文件前加include(“/safe.php“);即可 :

php防注入代码方法一:

<?php
//要过滤的非法字符
$ArrFiltrate=array(”‘”,”;”,”union”);
//出错后要跳转的url,不填则默认前一页
$StrGoUrl=””;
//是否存在数组中的值
function FunStringExist($StrFiltrate,$ArrFiltrate){
foreach ($ArrFiltrate as $key=>$value){
if (eregi($value,$StrFiltrate)){
return true;
}
}
return false;
}
//合并$_POST 和 $_GET
if(function_exists(array_merge)){
$ArrPostAndGet=array_merge($HTTP_POST_VARS,$HTTP_GET_VARS);
}else{
foreach($HTTP_POST_VARS as $key=>$value){
$ArrPostAndGet[]=$value;
}
foreach($HTTP_GET_VARS as $key=>$value){
$ArrPostAndGet[]=$value;
}
}
//验证开始
foreach($ArrPostAndGet as $key=>$value){
if (FunStringExist($value,$ArrFiltrate)){
echo “<script language=\”javascript\”>alert(\”非法字符\”);</script>”;
if (emptyempty($StrGoUrl)){
echo “<script language=\”javascript\”>history.go(-1);</script>”;
}else{
echo “<script language=\”javascript\”>window.location=\””.$StrGoUrl.”\”;</script>”;
}
exit;
}
}
?>

php防注入代码方法二:

/* 过滤所有GET过来变量 */
foreach ($_GET as $get_key=>$get_var)
{
if (is_numeric($get_var)) {
$get[strtolower($get_key)] = get_int($get_var);
} else {
$get[strtolower($get_key)] = get_str($get_var);
}
}
/* 过滤所有POST过来的变量 */
foreach ($_POST as $post_key=>$post_var)
{
if (is_numeric($post_var)) {
$post[strtolower($post_key)] = get_int($post_var);
} else {
$post[strtolower($post_key)] = get_str($post_var);
}
}
/* 过滤函数 */
//整型过滤函数
function get_int($number)
{
return intval($number);
}
//字符串型过滤函数
function get_str($string)
{
if (!get_magic_quotes_gpc()) {
return addslashes($string);
}
return $string;
}



+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



在现在各种黑客横行的时候,如何实现自己php代码安全,保证程序和服务器的安全是一个很重要的问题,我随便看了下关于php安全的资料,并不是很多,至少比asp少多了,呵呵,于是就想写点东西,来防止这些可能出现的情况。这里没有太深的技术含量,我只是比较简单的谈了谈。(以下操作如无具体说明,都是基于PHP+MySQL+Apache的情况)
    先来说说安全问题,我们首先看一下两篇文章:
http://www.xfocus.net/articles/200107/227.html     
http://www.xfocus.net/articles/200107/228.html

    上面文章是安全焦点上的关于PHP安全的文章,基本上比较全面的介绍了关于PHP的一些安全问题。

    在PHP编码的时候,如果考虑到一些比较基本的安全问题,首先一点:
1. 初始化你的变量

    为什么这么说呢?我们看下面的代码:
if ($admin)
{
    echo '登陆成功!';
    include('admin.php');
}
else
{
    echo '你不是管理员,无法进行管理!';
}

    好,我们看上面的代码好像是能正常运行,没有问题,那么加入我提交一个非法的参数过去呢,那么效果会如何呢?比如我们的这个页是 http://www.traget.com/login.php,那么我们提交:http://www.target.com/login.php?admin=1,呵呵,你想一些,我们是不是直接就是管理员了,直接进行管理。
    当然,可能我们不会犯这么简单错的错误,那么一些很隐秘的错误也可能导致这个问题,比如最近暴出来的phpwind 1.3.6论坛有个漏洞,导致能够直接拿到管理员权限,就是因为有个$skin变量没有初始化,导致了后面一系列问题。

    那么我们如何避免上面的问题呢?首先,从php.ini入手,把php.ini里面的register_global = off,就是不是所有的注册变量为全局,那么就能避免了。但是,我们不是服务器管理员,只能从代码上改进了,那么我们如何改进上面的代码呢?我们改写如下:
$admin = 0;      // 初始化变量
if ($_POST['admin_user'] && $_POST['admin_pass'])
{
    // 判断提交的管理员用户名和密码是不是对的相应的处理代码
    // ...
    $admin = 1;
}
else
{
    $admin = 0;
}

if ($admin)
{
    echo '登陆成功!';
    include('admin.php');
}
else
{
    echo '你不是管理员,无法进行管理!';
}

    那么这时候你再提交 http://www.target.com/login.php?admin=1 就不好使了,因为我们在一开始就把变量初始化为 $admin = 0 了,那么你就无法通过这个漏洞获取管理员权限。


2. 防止SQL Injection (sql注射)

    SQL 注射应该是目前程序危害最大的了,包括最早从asp到php,基本上都是国内这两年流行的技术,基本原理就是通过对提交变量的不过滤形成注入点然后使恶意用户能够提交一些sql查询语句,导致重要数据被窃取、数据丢失或者损坏,或者被入侵到后台管理。
基本原理我就不说了,我们看看下面两篇文章就很明白了:
http://www.4ngel.net/article/36.htm
http://www.4ngel.net/article/30.htm

    那么我们既然了解了基本的注射入侵的方式,那么我们如何去防范呢?这个就应该我们从代码去入手了。

    我们知道Web上提交数据有两种方式,一种是get、一种是post,那么很多常见的sql注射就是从get方式入手的,而且注射的语句里面一定是包含一些sql语句的,因为没有sql语句,那么如何进行,sql语句有四大句:
    select 、update、delete、insert,那么我们如果在我们提交的数据中进行过滤是不是能够避免这些问题呢?
    于是我们使用正则就构建如下函数:

/*
函数名称:inject_check()
函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
参        数:$sql_str: 提交的变量
返 回 值:返回检测结果,ture or false
函数作者:heiyeluren
*/
function inject_check($sql_str)
{
     return eregi('select|insert|update|delete|'|/*|*|../|./|union|into|load_file|outfile', $sql_str);    // 进行过滤
}

    我们函数里把 select,insert,update,delete, union, into, load_file, outfile /*, ./ , ../ , ' 等等危险的参数字符串全部过滤掉,那么就能够控制提交的参数了,程序可以这么构建:

<?php
if (inject_check($_GET['id']))
{
     exit('你提交的数据非法,请检查后重新提交!');
}
else
{
    $id = $_GET['id'];
    echo '提交的数据合法,请继续!';
}
?>
    假设我们提交URL为:http://www.target.com/a.php?id=1,那么就会提示:
    "提交的数据合法,请继续!"
    如果我们提交 http://www.target.com/a.php?id=1%27 select * from tb_name
    就会出现提示:"你提交的数据非法,请检查后重新提交!"

    那么就达到了我们的要求。

    但是,问题还没有解决,假如我们提交的是 http://www.target.com/a.php?id=1asdfasdfasdf 呢,我们这个是符合上面的规则的,但是呢,它是不符合要求的,于是我们为了可能其他的情况,我们再构建一个函数来进行检查:

/*
函数名称:verify_id()
函数作用:校验提交的ID类值是否合法
参        数:$id: 提交的ID值
返 回 值:返回处理后的ID
函数作者:heiyeluren
*/
function verify_id($id=null)
{
   if (!$id) { exit('没有提交参数!'); }    // 是否为空判断
   elseif (inject_check($id)) { exit('提交的参数非法!'); }    // 注射判断
   elseif (!is_numeric($id)) { exit('提交的参数非法!'); }    // 数字判断
   $id = intval($id);    // 整型化

   return  $id;
}

    呵呵,那么我们就能够进行校验了,于是我们上面的程序代码就变成了下面的:

<?php
if (inject_check($_GET['id']))
{
     exit('你提交的数据非法,请检查后重新提交!');
}
else
{
    $id = verify_id($_GET['id']);    // 这里引用了我们的过滤函数,对$id进行过滤
    echo '提交的数据合法,请继续!';
}
?>

    好,问题到这里似乎都解决了,但是我们有没有考虑过post提交的数据,大批量的数据呢?
    比如一些字符可能会对数据库造成危害,比如 ' _ ', ' % ',这些字符都有特殊意义,那么我们如果进行控制呢?还有一点,就是当我们的php.ini里面的magic_quotes_gpc = off 的时候,那么提交的不符合数据库规则的数据都是不会自动在前面加' '的,那么我们要控制这些问题,于是构建如下函数:

/*
函数名称:str_check()
函数作用:对提交的字符串进行过滤
参    数:$var: 要处理的字符串
返 回 值:返回过滤后的字符串
函数作者:heiyeluren
*/
function str_check( $str )
{
   if (!get_magic_quotes_gpc())    // 判断magic_quotes_gpc是否打开
   {
      $str = addslashes($str);    // 进行过滤
}
     $str = str_replace("_", "\_", $str);    // 把 '_'过滤掉
     $str = str_replace("%", "\%", $str);    // 把' % '过滤掉
   
   return $str;
}

OK,我们又一次的避免了服务器被沦陷的危险。

    最后,再考虑提交一些大批量数据的情况,比如发贴,或者写文章、新闻,我们需要一些函数来帮我们过滤和进行转换,再上面函数的基础上,我们构建如下函数:

/*
函数名称:post_check()
函数作用:对提交的编辑内容进行处理
参    数:$post: 要提交的内容
返 回 值:$post: 返回过滤后的内容
函数作者:heiyeluren
*/
function post_check($post)
{
   if (!get_magic_quotes_gpc())    // 判断magic_quotes_gpc是否为打开
   {
      $post = addslashes($post);    // 进行magic_quotes_gpc没有打开的情况对提交数据的过滤
   }
   $post = str_replace("_", "\_", $post);    // 把 '_'过滤掉
   $post = str_replace("%", "\%", $post);    // 把' % '过滤掉
   $post = nl2br($post);    // 回车转换
   $post= htmlspecialchars($post);    // html标记转换

   return $post;
}

    呵呵,基本到这里,我们把一些情况都说了一遍,其实我觉得自己讲的东西还很少,至少我才只讲了两方面,再整个安全中是很少的内容了,考虑下一次讲更多,包括php安全配置,apache安全等等,让我们的安全正的是一个整体,作到最安全。

    最后在告诉你上面表达的:1. 初始化你的变量  2. 一定记得要过滤你的变量



+++++++++++++++++++++++++++++++++++++++++++++

对POST或GET数据过滤

$_POST = sql_injection($_POST);
$_GET = sql_injection($_GET);

function sql_injection($content)
{
if (!get_magic_quotes_gpc()) {
if (is_array($content)) {
foreach ($content as $key=>$value) {
$content[$key] = addslashes($value);
}
} else {
addslashes($content);
}
}
return $content;
}


/*
函数名称:inject_check()
函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
参  数:$sql_str: 提交的变量
返 回 值:返回检测结果,ture or false
*/
function inject_check($sql_str) {
return eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $sql_str); // 进行过滤
}

/*
函数名称:verify_id()
函数作用:校验提交的ID类值是否合法
参  数:$id: 提交的ID值
返 回 值:返回处理后的ID
*/
function verify_id($id=null) {
if (!$id) { exit('没有提交参数!'); } // 是否为空判断
elseif (inject_check($id)) { exit('提交的参数非法!'); } // 注射判断
elseif (!is_numeric($id)) { exit('提交的参数非法!'); } // 数字判断
$id = intval($id); // 整型化

return $id;
}

/*
函数名称:str_check()
函数作用:对提交的字符串进行过滤
参  数:$var: 要处理的字符串
返 回 值:返回过滤后的字符串
*/
function str_check( $str ) {
if (!get_magic_quotes_gpc()) { // 判断magic_quotes_gpc是否打开
$str = addslashes($str); // 进行过滤
}
$str = str_replace("_", "\_", $str); // 把 '_'过滤掉
$str = str_replace("%", "\%", $str); // 把 '%'过滤掉

return $str;
}

/*
函数名称:post_check()
函数作用:对提交的编辑内容进行处理
参  数:$post: 要提交的内容
返 回 值:$post: 返回过滤后的内容
*/
function post_check($post) {
if (!get_magic_quotes_gpc()) { // 判断magic_quotes_gpc是否为打开
$post = addslashes($post); // 进行magic_quotes_gpc没有打开的情况对提交数据的过滤
}
$post = str_replace("_", "\_", $post); // 把 '_'过滤掉
$post = str_replace("%", "\%", $post); // 把 '%'过滤掉
$post = nl2br($post); // 回车转换
$post = htmlspecialchars($post); // html标记转换

return $post;
}
我分享,我成长!系统集成 XTJC.COM
2
 楼主| 发表于 2011-3-20 21:38:41 | 只看该作者
华为金牌代理
PHP编程要注意的安全问题,防注入问题。(应该反复学习,很有用!)

内容总结来源于互联网博客,PHP、MYSQL手册等。恕不一一注明来源。
//几个有用的php字符串处理函数

1 <?php
2
3 nl2br(); // \n to <br/>
4 addslashes(); stripslashes(); //对数据库操作时,转义特殊字符
5
6 chop(); //除去字符串右边空格
7 trim(); //除去字符串中所有空格
8 ltrim(); //除去字符串左边空格
9
10 htmlspecialchars(); //转换'$','"','<','>'为相应的html实体
11 htmlentities(); //转换所有html标记为相应的html实体
12 //两个函数在格式化带有英文字符的html代码的时候基本没啥问题,但是htmlentities对中文字符也不放过,这样得出来的结果是中文字符部分变为一堆乱码。

13 array explode(string separator, string str); //分割字符串
14 string implode(string separator, array arr); //连接字符串
15
16 strtoupper(); strtolower(); //转换大小写
17 ucfirst(); //只转换第一个字符为大写
18 ucwords(); //转换每个words的第一个字母为大写

19 mysql_real_escape_string()//本函数将 unescaped_string 中的特殊字符转义,并计及连接的当前字符集,因此可以安全用于 mysql_query()。 注: mysql_real_escape_string() 并不转义 % 和 _。

20 ?>


//防止sql注入

在PHP编码的时候,一些比较基本的安全问题

1. 注意初始化你的变量

2.防止SQL Injection (sql注射)

       我们知道Web上提交数据有两种方式,一种是get、一种是post,那么很多常见的sql注射就是从get方式入手的,而且注射的语句里面一定是包含一些sql语句的,因为没有sql语句,那么如何进行,sql语句有四大句:select 、update、delete、insert,那么我们如果在我们提交的数据中进行过滤是不是能够避免这些问题呢?

       于是我们使用正则就构建如下函数:


(1)把 select,insert,update,delete, union, into, load_file, outfile /*, ./ , ../ , ' 等等危险的参数字符串全部过滤掉,处理掉?id=1' select * from ***的情况

<?php

/*
函数名称:inject_check()
函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
参        数:$sql_str: 提交的变量
返 回 值:返回检测结果,ture or false
函数作者:heiyeluren
*/
function inject_check($sql_str) {
     return eregi('select|insert|update|delete|'|/*|*|../|./|union|into|load_file|outfile', $sql_str);    // 进行过滤
}

?>


(2)处理掉a.php?id=1asdfasdfasdf

<?php

/*
函数名称:verify_id()
函数作用:校验提交的ID类值是否合法
参        数:$id: 提交的ID值
返 回 值:返回处理后的ID
函数作者:heiyeluren
*/
function verify_id($id=null) {
   if (!$id) { exit('没有提交参数!'); }    // 是否为空判断
   elseif (inject_check($id)) { exit('提交的参数非法!'); }    // 注射判断
   elseif (!is_numeric($id)) { exit('提交的参数非法!'); }    // 数字判断
   $id = intval($id);    // 整型化
   return $id;
}

?>


(3)去除' _ ', ' % ',这些字符特殊意义字符

<?php

/*
函数名称:str_check()
函数作用:对提交的字符串进行过滤
参    数:$var: 要处理的字符串
返 回 值:返回过滤后的字符串
函数作者:heiyeluren
*/
function str_check( $str ) {
   if (!get_magic_quotes_gpc())    // 判断magic_quotes_gpc是否打开
   {
      $str = addslashes($str);    // 进行过滤
    }
     $str = str_replace("_", "\_", $str);    // 把 '_'过滤掉
     $str = str_replace("%", "\%", $str);    // 把' % '过滤掉
     return $str;
}

?>


(4)对编辑内容进行过滤和转换

<?php

/*
函数名称:post_check()
函数作用:对提交的编辑内容进行处理
参    数:$post: 要提交的内容
返 回 值:$post: 返回过滤后的内容
函数作者:heiyeluren
*/
function post_check($post) {
   if (!get_magic_quotes_gpc())    // 判断magic_quotes_gpc是否为打开
   {
      $post = addslashes($post);    // 进行magic_quotes_gpc没有打开的情况对提交数据的过滤
   }
   $post = str_replace("_", "\_", $post);    // 把 '_'过滤掉
   $post = str_replace("%", "\%", $post);    // 把' % '过滤掉
   $post = nl2br($post);    // 回车转换
   $post= htmlspecialchars($post);    // html标记转换
   return $post;
}

?>

综合来说即2个,1. 初始化你的变量   2. 一定记得要过滤你的变量


//服务器端用 mysql_real_escape_string 清洁客户端数据
在服务器端清洁客户端数据是每个程序员经常要做的工作,虽然我们通常会在客户端添加 Javascript 验证,但是,恶意用户很容易自己构造 FORM 提交数据以绕过客户端验证,另外,在客户端禁用 Javascript 时验证同样不能起到作用。因此,服务器端清洁数据必不可少,本文介绍的是用 mysql_real_escape_string 清洁数据的方法,经过清洁的数据可以直接插入到数据库中。



由于 mysql_real_escape_string 需要 MySQL 数据库连接,因此,在调用 mysql_real_escape_string 之前,必须连接上 MySQL 数据库。


PHP:

1 <?php
2 // 说明:用 array_map() 调用 mysql_real_escape_string 清理数组
3 // 整理:http://www.codebit.cn
4 function mysqlClean($data)
5 {
6     return (is_array($data))?array_map('mysqlClean', $data):mysql_real_escape_string($data);
7 }
8 ?>

调用方法
PHP:

1 <?php
2 $conn = mysql_connect('localhost', 'user', 'pass');
3
4
5 $_POST = mysqlClean($_POST);
6 ?>


经过清洁的数据可以直接插入数据库。


注意!mysql_real_escape_string 必须在(PHP 4 >= 4.3.0, PHP 5)的情况下才能使用。否则只能用 mysql_escape_string ,两者的区别是:

mysql_real_escape_string 考虑到连接的当前字符集,而mysql_escape_string 不考虑。


由于 mysql_real_escape_string 需要 MySQL 数据库连接,因此,在调用 mysql_real_escape_string 之前,必须连接上 MySQL 数据库。



在知道数据类型为字符串时,我们可以在清洁数据的同时限制字符串长度。此方法来自 David Lane, Hugh E. Williams《Web Database Application with PHP and MySQL 》(O'Reilly, May 2004)

PHP:

1 <?php
2 // 说明:用 mysql_real_escape_string 清洁并限制字符长度
3 // 整理:http://www.codebit.cn
4 function mysqlClean($array, $index, $maxlength)
5 {
6     if (isset($array[$index]))
7     {
8         $input = substr($array["{$index}"], 0, $maxlength);
9         $input = mysql_real_escape_string($input);
10         return ($input);
11     }
12     return NULL;
13 }
14 ?>

调用方法:
PHP:

1 <?php
2 $conn = mysql_connect('localhost', 'user', 'pass');
3
4 if(isset($_POST['username']))
5 {
6     $_POST['username'] = mysqlClean($_POST, 'username', 20);
7     echo $_POST['username'];
8 }
9 ?>

将 $_POST 数组中的 'username' 清洁并截取前20位字符。

//关于sql注入


SQL 注入攻击是黑客攻击网站最常用的手段。如果你的站点没有使用严格的用户输入检验,那么常容易遭到SQL注入攻击。SQL注入攻击通常通过给站点数据库提交不良的数据或查询语句来实现,很可能使数据库中的纪录遭到暴露,更改或被删除。下面来谈谈SQL注入攻击是如何实现的,又如何防范。

看这个例子:

// supposed input

$name = “ilia’; Delete FROM users;”;

mysql_query(“Select * FROM users Where name=’{$name}’”);

很明显最后数据库执行的命令是:

Select * FROM users Where name=ilia; Delete FROM users

这就给数据库带来了灾难性的后果--所有记录都被删除了。不过如果你使用的数据库是MySQL,那么还好,mysql_query()函数不允许直接执行这样的操作(不能单行进行多个语句操作),所以你可以放心。如果你使用的数据库是SQLite或者PostgreSQL,支持这样的语句,那么就将面临灭顶之灾了。

上面提到,SQL注入主要是提交不安全的数据给数据库来达到攻击目的。为了防止SQL注入攻击,PHP自带一个功能可以对输入的字符串进行处理,可以在较底层对输入进行安全上的初步处理,也即Magic Quotes。(php.ini magic_quotes_gpc)。如果magic_quotes_gpc选项启用,那么输入的字符串中的单引号,双引号和其它一些字符前将会被自动加上反斜杠\。

但Magic Quotes并不是一个很通用的解决方案,没能屏蔽所有有潜在危险的字符,并且在许多服务器上Magic Quotes并没有被启用。所以,我们还需要使用其它多种方法来防止SQL注入。许多数据库本身就提供这种输入数据处理功能。例如PHP的MySQL操作函数中有一个叫mysql_real_escape_string()的函数,可将特殊字符和可能引起数据库操作出错的字符转义。

看这段代码:

//如果Magic Quotes功用启用

if (get_magic_quotes_gpc()) {

$name = stripslashes($name);

}else{

$name = mysql_real_escape_string($name);

}

mysql_query(“Select * FROM users Where name=’{$name}’”);

注意,在我们使用数据库所带的功能之前要判断一下Magic Quotes是否打开,就像上例中那样,否则两次重复处理就会出错。如果MQ已启用,我们要把加上的\去掉才得到真实数据。除了对以上字符串形式的数据进行预处理之外,储存Binary数据到数据库中时,也要注意进行预处理。否则数据可能与数据库自身的存储格式相冲突,引起数据库崩溃,数据记录丢失,甚至丢失整个库的数据。有些数据库如PostgreSQL,提供一个专门用来编码二进制数据的函数pg_escape_bytea(),它可以对数据进行类似于Base64那样的编码。

如:

// for plain-text data use:

pg_escape_string($regular_strings);

// for binary data use:

pg_escape_bytea($binary_data);

另一种情况下,我们也要采用这样的机制。那就是数据库系统本身不支持的多字节语言如中文,日语等。其中有些的ASCII范围和二进制数据的范围重叠。不过对数据进行编码将有可能导致像LIKE abc% 这样的查询语句失效。




以下是一段copy来的代码:

PHP代码


$_POST = sql_injection($_POST);   
$_GET = sql_injection($_GET);   
   
function sql_injection($content)   
{   
if (!get_magic_quotes_gpc()) {   
if (is_array($content)) {   
foreach ($content as $key=>$value) {   
$content[$key] = addslashes($value);   
}   
} else {   
addslashes($content);   
}   
}   
return $content;   
}   

//用户发布的html,过滤危险代码
function uh($str)
{
$farr = array(
"/\s+/", //过滤多余的空白
"/<(\/?)(scripti?framestylehtmlbodytitlelinkmeta\?\%)([^>]*?)>/isU", //过滤 <script 等可能引入恶意内容或恶意改变显示布局的代码,如果不需要插入flash等,还可以加入<object的过滤
"/(<[^>]*)on[a-zA-Z]+\s*=([^>]*>)/isU", //过滤javascript的on事件

);
$tarr = array(
" ",
"<\\1\\2\\3>", //如果要直接清除不安全的标签,这里可以留空
"\\1\\2",
);

$str = preg_replace( $farr,$tarr,$str);
return $str;
}


//一个php操作mysql的脚本,带过滤XXS攻击的函数

<?php
class SFun{var $Fun="now()";function SFun($Date=""){if($Date!="")$this->Fun=$Date;}}
class MySqlDB
{
    var $dbConn;
    var $dbHost;
    var $dbUID;
    var $dbPWD;
    var $dbName;
    var $dbEncode;
var $IsOp;
    function MySqlDB($Host='localhost',$UID='root',$Pwd='',$Name='',$Encode='utf8')
{
   $this->dbHost = $Host;
        $this->dbUID = $UID;
        $this->dbPWD = $Pwd;
        $this->dbName = $Name;
        $this->dbEncode = $Encode;
   $this->IsOp=false;
    }

    //打开数据库
    function Open()
    {
        if (!$this->dbConn)
        {
            @$this->dbConn = mysql_connect($this->dbHost, $this->dbUID, $this->dbPWD) or die("数据库连接错误!...");
        }
        mysql_query("SET NAMES '" . $this->dbEncode . "'");
   mysql_select_db($this->dbName);
   $this->IsOp=true;
    }
    //关闭数据库
    function Close()
    {
        if ($this->dbConn&&$this->IsOp)
        {
            mysql_close($this->dbConn);
    $this->IsOp=false;
        }
    }
function htmlrsp($str)
{
   $str=str_replace("<","&lt;",$str);
   $str=str_replace(">","&gt;",$str);
   return $str;
}
//过滤XSS危险脚本
function RemoveXSS($val) {     
if(strpos($val,"<")===false)return $val;
$val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val);      
$search = 'abcdefghijklmnopqrstuvwxyz';   
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';   
$search .= '1234567890!@#$%^&*()';   
$search .= '~`";+/={}[]-_|\'\\';   
for ($i = 0; $i < strlen($search); $i++) {      
$val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;     
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;   
}   
$ra1 = array('javascript', 'vbscript', 'expression', 'applet');
$ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');   
$ra = array_merge($ra1, $ra2);
$val=preg_replace("'<script[^>]*?>.*?</script>'si","",$val);
$val=preg_replace("'<style[^>]*?>.*?</style>'si","",$val);
$val=preg_replace("/<(script|iframe|expression|applet|meta|xml|blink|style|frame|frameset|ilayer|layer|bgsound|title|base|link)(|[^>]+)>/i","",$val);
$val=preg_replace("/<\\/(script|iframe|expression|applet|meta|xml|blink|style|frame|frameset|ilayer|layer|bgsound|title|base)>/i","",$val);

$newval="";
$idx=0;
$edx=0;
do
{
   $idx=strpos($val,"<",$edx);
   if($idx!==false)
   {
    if($idx>$edx){
     $str=substr($val,$edx,$idx-$edx);
     $newval.=$this->htmlrsp($str);
     }
    $edx=strpos($val,">",$idx);
    if($edx!==false&&$edx>$idx)
    {
     $edx++;
     $tag=substr($val,$idx,$edx-$idx);
     $ridx=strrpos($tag,"<",1);
     if($ridx!==false){
      $str=substr($val,$idx,$ridx);
      $newval.=$this->htmlrsp($str);
      $idx=$idx+$ridx;
      $tag=substr($val,$idx,$edx-$idx);
      }
     
     if(stripos($tag,"on")===false)
     {$newval.=$tag;}
     else
     {
      for($j=0;$j<count($ra1);$j++){if(stripos($tag,$ra1[$j])!==false){$tag=str_ireplace($ra1[$j],"",$tag);}}
      for($j=0;$j<count($ra2);$j++){
       if(stripos($tag,$ra2[$j])!==false){
        $tag=preg_replace('/'.$ra2[$j].'="[^"]+"/i',"",$tag);
        $tag=preg_replace("/".$ra2[$j]."='[^']+'/i","",$tag);
        $tag=preg_replace("/".$ra2[$j]."=[^ ]+>/i",">",$tag);
        $tag=preg_replace("/".$ra2[$j]."=[^ ]+ /i","",$tag);
        $tag=str_ireplace($ra2[$j],"",$tag);
        }}
       $newval.=$tag;
     }
    }
    else
    {
     $str=substr($val,$idx);
     $newval.=$this->htmlrsp($str);
    }
   }
   else
   {
    $str=substr($val,$edx);
    $newval.=$this->htmlrsp($str);
   }
}while($idx!==false&&$edx!==false);
return $newval;
   }
   //执行SQL语句
   function ExeSql($sql)
   {
        mysql_query($sql, $this->dbConn) or die("执行SQL语句错误...".$sql);
   }
   function HGexecute($SqlArr)
   {
    mysql_query("SET AUTOCOMMIT=0");//设置为不自动提交,因为MYSQL默认立即执行
    mysql_query("BEGIN");//开始事务定义
    for($i=0;$i<count($SqlArr);$i++)
    {
     $sql=$SqlArr[$i];
           if(!mysql_query($sql, $this->dbConn))
     {
     mysql_query("ROLLBACK");//判断执行失败回滚
     mysql_query("SET AUTOCOMMIT=1");
     return false;
     }
   
    }
    mysql_query("SET AUTOCOMMIT=1");
       mysql_query("COMMIT");//执行事务
    return true;
   }

   //取SQL数据
    function GetData($sql)
    {
        $result = mysql_query($sql, $this->dbConn) or die("查询SQL语句错误...".$sql);
   $records=array();
   //while($record = mysql_fetch_array($result))
        while($record = mysql_fetch_object($result))
        {
            $records[] = $record;
        }
        return $records;
    }
function _T($str){
$str=$this->RemoveXSS($str);
$str=str_replace('\\','\\\\',$str);
$str=str_replace('\'','\\\'',$str);
//$str=str_replace('"','\\"',$str);
return $str;}
function GetOne($sql)
{
   $records=$this->GetData($sql);
        return $records[0];
}
function Like($str)
{
   $str=$this->_T($str);
   $str=str_replace('%','\\%',$str);
   return $str;
}
//插入记录
    function Add($Tb,$A,$IsId=false)
{
   $SqlKey=array();
   $SqlArr=array();
   foreach ($A as $key=>$value)
   {
     $SqlKey[]="`".$key."`";
   if($value===NULL)
   $SqlArr[]="NULL";
   else if(gettype($value)=="integer"||gettype($value)=="boolean"||gettype($value)=="double"||gettype($value)=="float")
   $SqlArr[]=$value;
   else if(gettype($value)=="object")
   $SqlArr[]=$value->Fun;
   else
   $SqlArr[]="'".$this->_T($value)."'";
   }
   $Sql="INSERT INTO ".$Tb."(".join(",",$SqlKey).") VALUES (".join(",",$SqlArr).")";
   mysql_query($Sql, $this->dbConn) or die("执行SQL语句错误...".$Sql);
   if($IsId)
   return mysql_insert_id($this->dbConn);
   else
   return 0;
}
function Update($Tb,$A,$Id)
{
   $Id=intval($Id);
   $SqlArr=array();
   foreach ($A as $key=>$value)
   {
    if($value===NULL)
    $SqlArr[]="`".$key."`=NULL";
    else if(gettype($value)=="integer"||gettype($value)=="boolean"||gettype($value)=="double"||gettype($value)=="float")
    $SqlArr[]="`".$key."`=".$value;
    else if(gettype($value)=="object")
    $SqlArr[]="`".$key."`=".$value->Fun;
    else
    $SqlArr[]="`".$key."`='".$this->_T($value)."'";
    }
    $Sql="update ".$Tb." set ".join(",",$SqlArr)." where id=".$Id;
    mysql_query($Sql, $this->dbConn) or die("执行SQL语句错误...".$Sql);
}
function Del($Tb,$Id)
{
   $Id=intval($Id);
   $Sql="delete from ".$Tb." where id=".$Id;
   mysql_query($Sql, $this->dbConn) or die("执行SQL语句错误...".$Sql);
}

}

/*


$DB=new MySqlDB('127.0.0.1','root','123','test');
$DB->Open();
$A["key1"]=$_POST["FCKeditor4"];
$A["key2"]=NULL;
$A["key3"]=new SFun("now()");
$A["key4"]=NULL;
$id=$DB->Add("test",$A,1);
echo $id;
$Row=$DB->GetOne("select * from test where id=".$id);
print_r($Row);
$DB->Close();*/

?>
我分享,我成长!系统集成 XTJC.COM
您需要登录后才可以回帖 登录 | 注册通行证

本版积分规则

联系我们| 手机版|系统集成论坛 ( 京ICP备11008917号 )

GMT+8, 2024-11-27 11:23 , Processed in 0.073744 second(s), 28 queries .

系统集成论坛

BBS.XTJC.COM

快速回复 返回顶部 返回列表