提问¶
问题1:什么是SQL注入攻击?
问题2:有几种简单方法防范SQL注入攻击?
问题3:mysql_real_escape_string对参数处理时,为什么必须加上单引号?
课程单元¶
什么是SQL注入攻击
SQL注入攻击的危害
SQL注入攻击示例
如何防范SQL注入攻击
1. 什么是SQL注入攻击¶
SQL注入攻击,指的是通过构建特殊的输入作为参数,传入后台处理程序(比如PHP代码),通过执行SQL语句,来执行攻击者所要的操作,从而达到攻击的目的。
SQL注入攻击危害严重,是网上最常见的攻击方式之一,一般写代码时不小心就很容易遗留漏洞,然而又非常容易防范。
2. SQL注入攻击的危害¶
攻击者可能可以绕过系统的安全措施,比如绕过登录帐号密码,直接进入系统。
攻击者可能可以任意读取、修改数据库中的数据。
攻击者可能可以通过对数据库进行操作,而取得登录系统的权限,直接进入系统,进行任意操作。
3. SQL注入攻击示例¶
这是一个简单的登录系统
<form action="login.php" method="post">
用户名:<input type="text" name="username"><br />
密码:<input type="password" name="password"><br />
<input type="submit" value="登录">
</form>
假设处理该登录操作的php代码如下:
<?php
$sql = "select * from user where username = '" . $_POST["username"] . "' and password = '" . $_POST["password"] . "'";
$query = mysql_query($sql);
if($rs = mysql_fetch_array($query)){
//这里执行登录成功的操作
echo "登录成功!";
}
else{
//这里执行登录失败的操作
echo "用户名或者密码错误,登录失败!";
}
我们在用户名输入框输入:admin’ or 1 = 1#,密码框输入123456,则产生了如下结果:
<?php
$_POST["username"] = "admin ' or 1 = 1#";
$_POST["password"] = "123456";
根据这个结果,生成的$sql是这样的:
<?php
$sql = "select * from user where username = 'admin' or 1 = 1#' and password = '123456'";
这个$sql的执行结果,会导致执行登录成功之后的操作。这样,我们就在没有用户名和密码的情况下,成功登录了系统。
同理,我们可以执行任意的其他数据库操作,一步步取出数据库全部数据,甚至取得系统登录权限。
4. 如何防范SQL注入攻击¶
1、最小权限原则
2、关闭错误提示
3、对查询参数进行转义
4、使用参数化查询
4.1. 最小权限原则¶
虚拟站点使用的数据库用户一定不要使用root等最高权限用户,必须针对每个虚拟站点单独设置一个用户,这个用户只能允许虚拟站点所在的ip登录,只能访问当前虚拟站点所使用的数据库,并且只有有限的少数几个权限。这样如果出现问题,可以尽可能将问题缩小在当前数据库之内,不至于扩大化引起其他数据库和其他站点问题。
4.2. 关闭错误提示¶
对于php,在php.ini中,设置:
display_errors = Off
如果没有修改php.ini的权限,在代码中设置:
ini_set(“display_errors”, “Off”);
这样如果代码出现问题,只会显示空白页面,不会显示详细的错误信息,减少泄漏代码和服务器配置情况的可能性。
4.3. 对查询参数进行转义¶
php提供了mysql_real_escape_string方法,用于转义 SQL 语句中使用的字符串中的特殊字符,可以安全用于 mysql_query()。
对于刚才的登录,我们将$sql加上mysql_real_escape_string改成如下格式:
<?php
$sql = "select * from user where username = '" . mysql_real_escape_string($_POST["username"]) . "' and password = '" . mysql_real_escape_string($_POST["password"]) . "'";
我们依然在用户名输入框输入:admin’ or 1 = 1#,密码框输入123456,此时结果如下:
<?php
$sql = "select * from user where username = 'admin' or 1 = 1#' and password = '123456';
这次我们成功防范了针对登录的SQL注入。
mysql_real_escape_string使用必须注意:
所有参数都必须使用该方法进行转义,包括$_GET、$_POST、$_SESSION、$_COOKIE
在参数前后必须加上单引号,下面的写法是正确的
<?php
$sql = "select * from user where username = '" . mysql_real_escape_string($_POST["username"]) . "' and password
下面的写法是错误的,起不到防范SQL注入效果
<?php
$sql = "select * from user where username = " . mysql_real_escape_string($_POST["username"]) . " and password
4.4. 使用参数化查询¶
建议采用mysqli取代传统的mysql方法。
对于刚才的登录操作,可以使用mysqli改成参数化查询,可以有效避免SQL注入问题
<?php
$sql = "select * from user where username = ? and password = ?";
$stmt = $mysqli_prepare($sql);
$stmt->bind_param("ss", $_POST["username"], $_POST["password"]);
$stmt->execute();
if($stmt->fetch()){
//这里执行登录成功的操作
echo "登录成功!";
}
else{
//这里执行登录失败的操作
echo "用户名或者密码错误,登录失败!";
}