魔术引号、addslashes和mysql_real_escape_string的防御以及绕过

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

0x00:php内置过滤函数

php有内置的函数用来防御攻击,简单的介绍几个函数。


魔术引号

当打开时,所有的 '(单引号),"(双引号),\(反斜线)和 NULL 字符都会被自动加上一个反斜线进行转义。这和 addslashes() 作用完全相同。


一共有三个魔术引号指令:

magic_quotes_gpc 影响到 HTTP 请求数据(GET,POST 和 COOKIE)。不能在运行时改变。在 PHP 中默认值为 on。 参见 get_magic_quotes_gpc()。

magic_quotes_runtime 如果打开的话,大部份从外部来源取得数据并返回的函数,包括从数据库和文本文件,所返回的数据都会被反斜线转义。该选项可在运行的时改变,在 PHP 中的默认值为 off。 参见 set_magic_quotes_runtime() 和 get_magic_quotes_runtime()。

magic_quotes_sybase 如果打开的话,将会使用单引号对单引号进行转义而非反斜线。此选项会完全覆盖 magic_quotes_gpc。如果同时打开两个选项的话,单引号将会被转义成 ''。而双引号、反斜线 和 NULL 字符将不会进行转义。 如何取得其值参见 ini_get()。


mysql_real_escape_string

转义sql语句中使用的字符串中的特殊字符:\x00、\n、\r、\、'、"、\x1a

addslashes()

返回在预定义字符之前添加反斜杠的字符串,预定义字符:'、"、\、NULL


看了很多php网站在防sql注入上还在使用ddslashes和str_replace,百度一下"PHP防注入"也同样在使用他们,实践发现就连mysql_real_escape_string也有黑客可以绕过的办法,如果你的系统仍在用上面三个方法,建议更好。

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


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

如果你不确定你的系统是否有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.5.44
int(3)
string(6) "latin1"
int(3)
string(6) "latin1"
int(0)
string(3) "gbk"

可以看出来不论是使用addslashes还是mysql_real_escape_string,我都可以利用编码的漏洞来实现输入任意密码就能登录服务器的注入攻击!!!!



0x01:宽字节注入

  尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范。但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,比如gbk,作为自己默认的编码类型。也有一些cms为了考虑老用户,所以出了gbk和utf-8两个版本。


我们就以gbk字符编码为示范,拉开帷幕。gbk是一种多字符编码,有一个地方尤其要注意:

通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。在php中,我们可以通过输出

echo strlen("和");

来测试。当将页面编码保存为gbk时输出2,utf-8时输出3。


除了gbk以外,所有ANSI编码都是2个字节。ansi只是一个标准,在不用的电脑上它代表的编码可能不相同,比如简体中文系统中ANSI就代表是GBK。


如上,想绕过魔术引号等限制,有两种方式:

1.将\前面再加一个\(或单数个即可),变成\\’,这样\被转义了,’逃出了限制

2.将\去掉。


我们这里的宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)。

这就是mysql的特性,因为gbk是多字节编码,他认为两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“運”,而’逃逸了出来。再尝试“%df%df%27”,就不报错了。因为%df%df是一个汉字,%5c%27不是汉字,仍然是\’。


那么mysql怎么判断一个字符是不是汉字,根据gbk编码,第一个字节ascii码大于128,基本上就可以了。比如我们不用%df,用%a1也可以,%a1%5c他可能不是汉字,但一定会被mysql认为是一个宽字符,就能够让后面的%27逃逸了出来。


但需要注意的是

gb2312和gbk应该都是宽字节家族的一员,却不能注入,这归结于gb2312编码的取值范围。它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE,而\是0x5c,是不在低位范围中的。所以,0x5c根本不是gb2312中的编码,所以自然也是不会被吃掉的。


所以,把这个思路扩展到世界上所有多字节编码,我们可以这样认为:只要低位的范围中含有0x5c的编码,就可以进行宽字符注入。



0x02:宽字符注入的修复


先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string来过滤用户输入。


这个方式是可行的,但有部分老的cms,在多处使用addslashes来过滤字符串,我们不可能去一个一个把addslashes都修改成mysql_real_escape_string。我们第二个解决方案就是,将character_set_client设置为binary(二进制)。


只需在所有sql语句前指定一下连接的形式是二进制:


mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn); 


这几个变量是什么意思?


当我们的mysql接受到客户端的数据后,会认为他的编码是character_set_client,然后会将之将换成character_set_connection的编码,然后进入具体表和字段后,再转换成字段对应的编码。


然后,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。


所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。



0x03:PDO和MYSQLI

完美解决方案就是使用拥有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
}
本文转自 wt7315 51CTO博客,原文链接:http://blog.51cto.com/wt7315/1931667



相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9月前
|
PHP
php获取随机字符串,可设置位数以及大小写
php获取随机字符串,可设置位数以及大小写
54 0
|
6月前
|
安全
格式化字符串漏洞(Format String Attack)
格式化字符串漏洞(Format String Attack)
41 0
|
8月前
|
SQL
sql server从某个字符开始截取后面的字符串
sql server从某个字符开始截取后面的字符串
130 0
|
8月前
|
数据采集 前端开发 关系型数据库
小写转换:深入了解MySQL中的LOWER()函数
在数据库操作中,将字符串转换为小写形式是常见的需求,这时可以使用MySQL中的LOWER()函数。本文将深入探讨LOWER()函数的用法、示例以及在数据库操作中的应用。
292 0
|
8月前
|
数据采集 前端开发 关系型数据库
修剪字符串:深入了解MySQL中的TRIM()函数
在数据库操作中,去除字符串首尾的空格或特定字符是常见的需求,这时可以使用MySQL中的TRIM()函数。本文将深入探讨TRIM()函数的用法、示例以及在数据库操作中的应用。
211 0
|
9月前
|
SQL 关系型数据库 MySQL
php替换mysql数据库中逗号组成的字符串的解决方案
php替换mysql数据库中逗号组成的字符串的解决方案
57 0
|
SQL 存储 安全
MySQL的mysql_real_escape_string()函数是干什么的?底层原理是什么?
MySQL的mysql_real_escape_string()函数是干什么的?底层原理是什么?
444 0
|
SQL 关系型数据库 MySQL
mysql的安全漏洞的一种现象,就是利用转义字符把 ' ' 化没了,然后true 起作用啦
mysql的安全漏洞的一种现象,就是利用转义字符把 ' ' 化没了,然后true 起作用啦
115 0
mysql的安全漏洞的一种现象,就是利用转义字符把 ' ' 化没了,然后true 起作用啦