PHP Conference Japan 2024

mysql_real_escape_string

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string转义字符串中用于 SQL 语句的特殊字符

警告

此扩展在 PHP 5.5.0 中已弃用,并在 PHP 7.0.0 中移除。应改用 MySQLiPDO_MySQL 扩展。另请参阅 MySQL:选择 API 指南。此函数的替代方法包括

描述

mysql_real_escape_string(string $unescaped_string, resource $link_identifier = NULL): string

转义 unescaped_string 中的特殊字符,并考虑连接的当前字符集,以便可以安全地将其放置在 mysql_query() 中。如果要插入二进制数据,则必须使用此函数。

mysql_real_escape_string() 调用 MySQL 的库函数 mysql_real_escape_string,该函数在以下字符前面加上反斜杠:\x00\n\r\'"\x1a

在将数据发送到 MySQL 进行查询之前,必须始终(除少数例外情况外)使用此函数使数据安全。

注意

安全:默认字符集

必须在服务器级别或使用 API 函数 mysql_set_charset() 设置字符集,才能使其影响 mysql_real_escape_string()。有关更多信息,请参阅关于 字符集 的概念部分。

参数

unescaped_string

要转义的字符串。

link_identifier

MySQL 连接。如果未指定链接标识符,则假定为 mysql_connect() 最近打开的链接。如果找不到此类链接,它将尝试创建一个链接,就像 mysql_connect() 未带任何参数被调用一样。如果未找到或建立连接,则会生成 E_WARNING 级别错误。

返回值

返回转义后的字符串,或者在出错时返回 false

错误/异常

在没有 MySQL 连接的情况下执行此函数也会发出 E_WARNING 级别 PHP 错误。仅在存在有效的 MySQL 连接时执行此函数。

示例

示例 #1 简单 mysql_real_escape_string() 示例

<?php
// 连接
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
OR die(
mysql_error());

// 查询
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>

示例 #2 mysql_real_escape_string() 需要连接示例

此示例演示在调用此函数时如果没有 MySQL 连接会发生什么。

<?php
// 我们尚未连接到 MySQL

$lastname = "O'Reilly";
$_lastname = mysql_real_escape_string($lastname);

$query = "SELECT * FROM actors WHERE last_name = '$_lastname'";

var_dump($_lastname);
var_dump($query);
?>

上面的示例将输出类似以下内容

Warning: mysql_real_escape_string(): No such file or directory in /this/test/script.php on line 5
Warning: mysql_real_escape_string(): A link to the server could not be established in /this/test/script.php on line 5

bool(false)
string(41) "SELECT * FROM actors WHERE last_name = ''"

示例 #3 SQL 注入攻击示例

<?php
// 我们没有检查 $_POST['password'],它可能是用户想要的任何内容!例如:
$_POST['username'] = 'aidan';
$_POST['password'] = "' OR ''='";

// 查询数据库以检查是否存在任何匹配的用户
$query = "SELECT * FROM users WHERE user='{$_POST['username']}' AND password='{$_POST['password']}'";
mysql_query($query);

// 这意味着发送到 MySQL 的查询将是:
echo $query;
?>

发送到 MySQL 的查询

SELECT * FROM users WHERE user='aidan' AND password='' OR ''=''

这将允许任何人无需有效密码即可登录。

备注

注意:

在使用 mysql_real_escape_string() 之前需要 MySQL 连接,否则会生成 E_WARNING 级别错误,并返回 false。如果未定义 link_identifier,则使用最后一个 MySQL 连接。

注意:

如果不使用此函数转义数据,则查询容易受到 SQL 注入攻击

注意 mysql_real_escape_string() 不会转义 %_。如果与 LIKEGRANTREVOKE 结合使用,这些字符在 MySQL 中是通配符。

另请参阅

添加注释

用户贡献的注释 6 条注释

feedr
14 年前
只是一个模仿原始 mysql_real_escape_string 但不需要活动 mysql 连接的小函数。可以作为数据库类中的静态函数实现。希望它能帮到某些人。

<?php
function mysql_escape_mimic($inp) {
if(
is_array($inp))
return
array_map(__METHOD__, $inp);

if(!empty(
$inp) && is_string($inp)) {
return
str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
}

return
$inp;
}
?>
nicolas
18 年前
请注意,mysql_real_escape_string 不会在文档中提到的 \x00、\n、\r 和 \x1a 前面添加反斜杠,而是实际用 MySQL 可接受的查询表示替换字符(例如,\n 被替换为 '\n' 字面量)。(\, ' 和 " 按照文档进行转义)这不会改变您应该如何使用此函数,但我认为了解这一点很有用。
Walter Tross
12 年前
更多信息
https://dev.mysqlserver.cn/doc/refman/5.5/en/mysql-real-escape-string.html
(在 URL 中替换您的 MySQL 版本)
sam at numbsafari dot com
12 年前
关于转义的任何讨论都不能不告诉每个人,您基本上永远不应该使用外部输入来生成解释代码。这适用于 SQL 语句或您对任何类型的“eval”函数调用的任何内容。

因此,不要使用这个非常糟糕的函数,而是使用参数化预处理语句。

老实说,使用用户提供的数据来组成 SQL 语句应该被视为职业过失,您应该对未使用参数化预处理语句而被您的雇主或客户追究责任。

这是什么意思?

这意味着不要像这样构建 SQL 语句

"INSERT INTO X (A) VALUES(".$_POST["a"].")"

您应该使用 mysqli 的 prepare() 函数(https://php.net/manual/en/mysqli.prepare.php)来执行如下所示的语句

"INSERT INTO X (A) VALUES(?)"

注意:这并不意味着您永远不应该生成动态 SQL 语句。这意味着您永远不应该使用用户提供的数据来生成这些语句。任何用户提供的数据都应在语句准备后作为参数传递。

因此,例如,如果您正在构建一个小框架并希望根据请求 URI 对表进行插入操作,那么您最好不要获取 $_SERVER['REQUEST_URI'] 值(或其任何部分)并将其直接与您的查询连接起来。相反,您应该解析出您想要的 $_SERVER['REQUEST_URI'] 值的一部分,并将其通过某种函数或关联数组映射到非用户提供的值。如果映射未生成任何值,则表示用户提供的数据存在问题。

未能遵循此操作是 Ruby On Rails 框架中许多 SQL 注入问题的根源,即使它使用了参数化预处理语句。这就是 GitHub 曾经被黑客入侵的方式。因此,没有任何语言能幸免于此问题。这就是为什么这是一个通用的最佳实践,而不是特定于 PHP 的东西,以及为什么您应该真正采用它。

此外,即使使用参数化预处理语句,您也应该对用户提供的数据进行某种验证。这是因为用户提供的数据通常会成为某些生成的 HTML 的一部分,并且您希望确保用户提供的数据不会在浏览器中造成安全问题。
rohankumar dot 1524 at gmail dot com
3 年前
对于使用 `mysql_escape_string` 的旧项目,需要升级 PHP 版本到 7 及以上。基本上,这发生在维护项目中,我们不知道应用程序中使用了多少个文件这些函数。我们可以使用 [mysqli.real-escape-string][1] 来代替该函数

如果您有一个典型的连接文件,例如 `conn.php`

$conn = new mysqli($host, $user, $password, $db);
// 可能还有几行用于处理 $conn
if (!function_exists('mysql_escape_string')) {
function mysql_escape_string($sting){ // 如果 mysql_escape_string 不可用
return $conn->real_escape_string($string); // 使用 $conn 实例进行转义
}
}

[1]: https://php.net/manual/en/mysqli.real-escape-string.php
strata_ranger at hotmail dot com
14 年前
示例 #2 中关于 SQL 注入有一个有趣的怪癖:AND 优先于 OR,因此注入的查询实际上执行为 WHERE (user='aidan' AND password='') OR ''='', 所以它不会返回对应于任意用户名(在本例中为 'aidan')的数据库记录,而实际上会返回所有数据库记录。无特定顺序。因此,攻击者可能能够以任何帐户的身份登录,但不一定能够控制它是哪个帐户。

当然,潜在的攻击者可以简单地修改他们的参数来针对感兴趣的特定用户

<?php

// 例如,攻击者的值
$_POST['username'] = '';
$_POST['password'] = "' OR user = 'administrator' AND '' = '";

// 格式错误的查询
$query = "SELECT * FROM users WHERE user='$_POST[username]' AND password='$_POST[password]'";

echo
$query;

// 发送到 MySQL 的查询将读取:
// SELECT * FROM users WHERE user='' AND password='' OR user='administrator' AND ''='';
// 这将允许任何人访问名为 'administrator' 的帐户

?>
To Top