防止sql注入-PHP防SQL注入不要再用addslashes和htmlspecialchars()和addslashes()函数了

版权声明: https://blog.csdn.net/yetaodiao/article/details/86153087

A、首先说说htmlspecialchars()和addslashes()函数吧:

在防止被注入攻击时,常会用到两个函数:htmlspecialchars()和addslashes()函数。这两个函数都是对特殊字符进行转义。

1)addslashes()作用及使用

addslashes()通常用于防止sql注入,它可对通过get,post和cookie传递过来的参数的单引号和双引号已经null前加“\”进行转义

如:如变量$str=$_POST["str"];的值为:bb' or 1='1。通过addslashes()函数过滤后会变为:bb\' or 1=\'1;

2)htmlspecialchars()作用及使用

htmlspecialchars()也是对字符进行转义,与addslashes()不同的是htmlspecialchars()是将特殊字符用引用实体替换。

如<script>alert('xss')</script>通过htmlspecialchars()过滤后为&lt;script&gt;alert(&#039;xss&#039;)&lt;/script&gt

3)addslashes()与htmlspecialchars()的区别

除了两个函数的转义方式不同外,它们的使用也不同。

addslashes()通过用于防止sql语句注入,在执行sql语句前对通过get、post和cookie传递来的参数中的单引号,双引号,\ 和null进行转义。

但sql执行成功后,插入到数据库中的数据是不带有转义字符\的。这是如果插入到数据库中的是一些js脚本,当这些脚本被读取出来时还是会被执行。

这是我们可对读取出来的数据使用htmlspecialchars()进行过滤,避免执行被注入的脚本。

B、很多PHP网站在防SQL注入上还在使用addslashes和str_replace,百度一下"PHP防注入"也同样在使用他们,实践发现就连mysql_real_escape_string也有黑客可以绕过的办法,如果你的系统仍在用上面三个方法,那么我的这篇博文就有了意义,以提醒所有后来者绕过这个坑。

出于为后人栽树而不是挖坑的考虑,给出PHP以及MYSQL的版本信息,以免将来“问题”不再是“问题”了。

用str_replace以及各种php字符替换函数来防注入已经不用我说了,这种“黑名单”式的防御已经被证明是经不起时间考验的。

下面给出绕过addslasher和mysql_real_escape_string的方法(Trick)。

注意:虽然在MYSQL5.5.37-log下该Trick已经被修复了,但仍然没有确切地解决注入问题,介于很多公司的系统仍在使用Mysql5.0,我建议立马做出改进。

注意:如果你不确定你的系统是否有SQL注入的风险,请将下面的下面的DEMO部署到你的服务器,如果运行结果相同,那么请参考最后的完美的解决方案

美术高考培训     福州美术培训     北京美术培训班

高考美术培训班 厦门美术培训     深圳美术培训    

北京高考美术培训   沈阳美术培训     西安美术培训学校  

长春美术培训班 中央美术学院培训班     北京美术高考培训班    

幼儿美术培训     太原美术培训     重庆高考美术培训  

长春美术培训     高考美术培训学校   成都美术培训学校  

清华美院美术培训 中央美院美术培训 河北美术培训班

MYSQL:

mysql> select version();
+---------------------+
| version()           |
+---------------------+
| 5.0.45-community-ny |
+---------------------+
1 row in set (0.00 sec)
mysql> create database test default charset GBK;
Query OK, 1 row affected (0.00 sec)
mysql> use test;
Database changed
mysql> CREATE TABLE users (
    username VARCHAR(32) CHARACTER SET GBK,
    password VARCHAR(32) CHARACTER SET GBK,
    PRIMARY KEY (username)
);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into users SET username='ewrfg', password='wer44';
Query OK, 1 row affected (0.01 sec)
mysql> insert into users SET username='ewrfg2', password='wer443';
Query OK, 1 row affected (0.01 sec)
mysql> insert into users SET username='ewrfg4', password='wer4434';
Query OK, 1 row affected (0.01 sec)=

PHP:

<?php
echo "PHP version: ".PHP_VERSION."\n";

mysql_connect('servername','username','password');
mysql_select_db("test");
mysql_query("SET NAMES GBK");

$_POST['username'] = chr(0xbf).chr(0x27).' OR username = username /*';
$_POST['password'] = 'guess';

$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);
$sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);

var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());

$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);

var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());

mysql_set_charset("GBK");
$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);

var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());


结果:

PHP version: 5.2.5
int(3)
string(6) "latin1"
int(3)
string(6) "latin1"
int(0)
string(3) "gbk" 

可以看出来不论是使用addslashes还是mysql_real_escape_string,我都可以利用编码的漏洞来实现输入任意密码就能登录服务器的注入攻击!!!!(攻击的原理我就不多说了,感兴趣的同学可以研究下字符编码中单字节和多字节的问题)

注意:第三个mysql_real_escape_string之所以能够防注入是因为mysql_escape_string本身并没办法 判断当前的编码,必须同时指定服务端的编码和客户端的编码,加上就能防编码问题的注入了。虽然是可以一定程度上防止SQL注入,但还是建议以下的完美解决方案

完美解决方案就是使用拥有Prepared Statement机制的PDO和MYSQLi来代替mysql_query(注:mysql_query自 PHP 5.5.0 起已废弃,并在将来会被移除):

PDO:

$pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute(array('name' => $name));

foreach ($stmt as $row) {
    // do something with $row
}

MYSQLi:

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);

$stmt->execute();

$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // do something with $row
}

猜你喜欢

转载自blog.csdn.net/yetaodiao/article/details/86153087