PHP防SQL注入不要再用addslashes和mysql_real_escape_string了

本文揭示了使用addslashes和mysql_real_escape_string等方法在防止SQL注入方面的局限性,并通过实例展示了如何利用编码漏洞进行攻击。文章最后给出了使用PDO和MYSQLi的PreparedStatement机制这一更安全的解决方案。

作者:深蓝的镰刀

链接:https://blog.youkuaiyun.com/hornedreaper1988/article/details/43520257
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


看了很多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:

[sql]  view plain  copy
  1. mysql> select version();  
  2. +---------------------+  
  3. | version()           |  
  4. +---------------------+  
  5. | 5.0.45-community-ny |  
  6. +---------------------+  
  7. 1 row in set (0.00 sec)  
  8. mysql> create database test default charset GBK;  
  9. Query OK, 1 row affected (0.00 sec)  
  10. mysql> use test;  
  11. Database changed  
  12. mysql> CREATE TABLE users (  
  13.     username VARCHAR(32) CHARACTER SET GBK,  
  14.     password VARCHAR(32) CHARACTER SET GBK,  
  15.     PRIMARY KEY (username)  
  16. );  
  17. Query OK, 0 rows affected (0.02 sec)  
  18. mysql> insert into users SET username='ewrfg'password='wer44';  
  19. Query OK, 1 row affected (0.01 sec)  
  20. mysql> insert into users SET username='ewrfg2'password='wer443';  
  21. Query OK, 1 row affected (0.01 sec)  
  22. mysql> insert into users SET username='ewrfg4'password='wer4434';  
  23. Query OK, 1 row affected (0.01 sec)=  

PHP:

[php]  view plain  copy
  1. <?php  
  2. echo "PHP version: ".PHP_VERSION."\n";  
  3.   
  4. mysql_connect('servername','username','password');  
  5. mysql_select_db("test");  
  6. mysql_query("SET NAMES GBK");  
  7.   
  8. $_POST['username'] = chr(0xbf).chr(0x27).' OR username = username /*';  
  9. $_POST['password'] = 'guess';  
  10.   
  11. $username = addslashes($_POST['username']);  
  12. $password = addslashes($_POST['password']);  
  13. $sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";  
  14. $result = mysql_query($sqlor trigger_error(mysql_error().$sql);  
  15.   
  16. var_dump(mysql_num_rows($result));  
  17. var_dump(mysql_client_encoding());  
  18.   
  19. $username = mysql_real_escape_string($_POST['username']);  
  20. $password = mysql_real_escape_string($_POST['password']);  
  21. $sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";  
  22. $result = mysql_query($sqlor trigger_error(mysql_error().$sql);  
  23.   
  24. var_dump(mysql_num_rows($result));  
  25. var_dump(mysql_client_encoding());  
  26.   
  27. mysql_set_charset("GBK");  
  28. $username = mysql_real_escape_string($_POST['username']);  
  29. $password = mysql_real_escape_string($_POST['password']);  
  30. $sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";  
  31. $result = mysql_query($sqlor trigger_error(mysql_error().$sql);  
  32.   
  33. var_dump(mysql_num_rows($result));  
  34. var_dump(mysql_client_encoding());  

结果:

[plain]  view plain  copy
  1. PHP version: 5.2.5  
  2. int(3)  
  3. string(6) "latin1"  
  4. int(3)  
  5. string(6) "latin1"  
  6. int(0)  
  7. 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:

[php]  view plain  copy
  1. $pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8''user''pass');  
  2.   
  3. $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
  4. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  
  5. $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');  
  6. $stmt->execute(array('name' => $name));  
  7.   
  8. foreach ($stmt as $row) {  
  9.     // do something with $row  
  10. }  

MYSQLi:

[php]  view plain  copy
  1. $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');  
  2. $stmt->bind_param('s'$name);  
  3.   
  4. $stmt->execute();  
  5.   
  6. $result = $stmt->get_result();  
  7. while ($row = $result->fetch_assoc()) {  
  8.     // do something with $row  
  9. }  

<think>好的,用户正在寻找SQL注入护相关的内置过滤函数,特别是提到了mysql_escape_string、str_replace、intval、addslashes这几个函数。我需要根据提供的引用内容,整理出这些函数的使用方法注意事项。 首先,我需要回顾每个引用中的相关信息。引用1提到mysql_real_escape_string必须在PHP4>=4.3.0PHP5中使用,否则用mysql_escape_string,但两者有区别,前者考虑连接字符集。引用2指出,即使使用PDO的模拟预处理,如果PHP版本低且未指定charset,仍然可能有注入漏洞,建议升级PHP并设置charset。引用4展示了一个post_check函数,里面用到了addslashes、str_replace等函数,并处理了magic_quotes_gpc的情况。引用5给出了使用mysql_real_escape_string的示例。引用3提到了MySQL的报错函数,可能注入攻击有关,但用户的问题是关于护函数,所以这部分可能暂时不用。 接下来,我需要将每个函数的信息整理出来。mysql_real_escape_string需要考虑连接字符集,比mysql_escape_string更安全,但已弃用。addslashes用于转义特殊字符,但依赖于magic_quotes_gpc的设置,引用4中的函数处理了这个情况。str_replace用于过滤特定字符,如%_。intval用于确保数值类型,防止注入。同时,PDO的参数绑定预处理语句是更推荐的方法,如引用2提到的设置EMULATE_PREPARES为false,并指定charset。 需要注意的是,用户可能想知道这些函数的适用场景、限制以及最佳实践。比如,虽然某些函数可以过滤,但单独使用可能不够,需要组合其他方法。例如,引用4中的函数不仅用了addslashes,还处理了magic_quotes_gpc,替换了%_,并进行HTML转义。这可能超出SQL注入的范围,但用户可能关心整体输入过滤。 另外,用户提到的函数中有mysql_escape_string,但根据引用1,这个函数已经过时,推荐使用mysql_real_escape_string,而后者在PHP7中也被移除,所以最终应该建议使用PDO或MySQLi的参数化查询。 可能还需要提到,过滤函数虽然有用,但参数化查询才是更根本的解决方案,因为过滤容易遗漏,而参数化能从根本上分离数据指令。引用2中提到的PDO设置就是一个例子。 总结:需要分点列出每个函数的作用、注意事项,并强调参数化查询的重要性。同时,引用相关的内容来支持每个点,比如引用15关于mysql_real_escape_string,引用4关于addslashesstr_replace,引用2关于PDO的设置。</think>### SQL注入护常用内置过滤函数及注意事项 #### 1. **`mysql_real_escape_string`** 用于转义字符串中的特殊字符(如单引号、双引号、反斜杠等),需基于当前数据库连接的字符集处理[^1]。 - **适用场景**:拼接SQL语句时对字符串参数进行转义。 - **注意事项**: - 仅在PHP 4.3.0+PHP 5+版本可用,且需确保已建立数据库连接。 - PHP 7中已弃用,建议改用参数化查询(如PDO或MySQLi)。 示例: ```php $username = mysql_real_escape_string($_POST['username']); $sql = "SELECT * FROM users WHERE username='$username'"; ``` --- #### 2. **`addslashes`** 自动转义单引号、双引号、反斜杠NULL字符,但依赖`magic_quotes_gpc`配置[^4]。 - **适用场景**:兼容旧代码时,需手动判断`magic_quotes_gpc`状态[^4]。 - **注意事项**: - 若`magic_quotes_gpc=On`,重复转义会导致数据错误。 - 无法御多字节编码注入(如GBK宽字节漏洞)。 示例(结合`magic_quotes_gpc`判断): ```php if (!get_magic_quotes_gpc()) { $input = addslashes($input); } ``` --- #### 3. **`str_replace`** 用于过滤特定危险字符(如`%`、`_`),常用于模糊查询场景。 - **适用场景**:防止LIKE子句中的通配符滥用。 - **注意事项**: - 需手动定义需过滤的字符列表。 - 不能替代参数化查询或转义函数。 示例: ```php $input = str_replace(array("%", "_"), array("\%", "\_"), $input); ``` --- #### 4. **`intval`** 将变量强制转换为整数类型,用于处理数值型参数[^5]。 - **适用场景**:确保ID、年龄等参数为整数。 - **注意事项**: - 仅适用于纯数字输入,对字符串无效。 示例: ```php $id = intval($_GET['id']); $sql = "SELECT * FROM articles WHERE id=$id"; ``` --- #### 5. **`PDO参数化查询`**(推荐) 通过预处理语句分离数据与SQL指令,从根本上注入[^2]。 - **注意事项**: - 需设置`PDO::ATTR_EMULATE_PREPARES=false`并指定DSN字符集,避免模拟预处理漏洞。 示例: ```php $pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass'); $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email"); $stmt->execute(['email' => $email]); ``` --- ### 综合建议 1. **优先使用参数化查询**(PDO或MySQLi),避免手动拼接SQL[^2]。 2. 若必须拼接SQL,组合使用过滤函数(如`intval`+`mysql_real_escape_string`)并严格校验输入格式。 3. 升级到PHP 5.3.6+,并在DSN中指定字符集(如`charset=utf8`)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值