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 中的通配符。

另请参见

添加注释

用户贡献注释 10 个注释

184
feedr
13 年前
只是一个模拟原始 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;
}
?>
22
Walter Tross
12 年前
更多信息
https://dev.mysqlserver.cn/doc/refman/5.5/en/mysql-real-escape-string.html
(在 URL 中替换您的 MySQL 版本)
31
nicolas
18 年前
请注意,mysql_real_escape_string 不会在文档中提到的 \x00、\n、\r 和 \x1a 前面添加反斜杠,而是将字符替换为 MySQL 可接受的查询表示形式(例如,\n 将被替换为 '\n' 字面量)。(\、' 和 " 按文档进行转义)这不会改变您使用此函数的方式,但我认为了解这一点很重要。
8
sam at numbsafari dot com
11 年前
关于转义的任何讨论都必须告诉大家,您基本上永远不应该使用外部输入来生成解释的代码。这适用于 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 的一部分,您希望确保用户提供的數據不会在浏览器中造成安全问题。
-2
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
1
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' 的帐户

?>
-14
plgs at ozemail dot com dot au
15 年前
不要忘记,如果您使用的是 Mysqli(即“改进的”Mysql 扩展),那么您需要使用相应的 mysqli 函数 mysqli_real_escape_string()。参数顺序也不同。
-8
jonnie
7 年前
引用 Sam at Numb Safari 的话

[ "关于转义的任何讨论都必须告诉大家,您基本上永远不应该使用外部输入来生成解释的代码。这适用于 SQL 语句,或者您对任何类型的“eval”函数进行的任何操作。

因此,与其使用这个非常糟糕的函数,不如使用参数化的预处理语句。

老实说,使用用户提供的數據來編寫 SQL 語句應被視為職業疏忽,您应该因未使用参数化的预处理语句而被雇主或客户追究责任。" ]

Sam 说得对........

但是我认为停止所有清理并简单地将任务传递给参数化的预处理语句是不明智的。

在特定情况下工作的特定开发人员始终会更多地了解有效输入(特定于该上下文)。

如果您要求用户传入一个您已经提供给他们的值,并且您知道所有这些值的开头都是 AB******,并且字符串的长度应为 7 或 11,但不能是其他任何长度,那么您就有了一个良好的预清理器 - 字符串的不同允许长度可能表明遗留数据。

我绝不想简单地将恶意用户可能通过表单传入的垃圾数据传递给参数化的预处理语句,我总是希望先进行自己的健全性检查,在某些情况下,这些检查可能会过分谨慎,并简单地选择完全中止数据库操作。

这样,我的数据库就不会被安全化的不安全语句阻塞 - 它根本不会被阻塞,这更好。

分层安全 - 应该在使用预处理语句之前始终考虑清理和验证。

此外,据我从官方文档中了解到
==============================================

"转义和 SQL 注入

绑定变量与查询分开发送到服务器,因此不会干扰它。服务器在语句模板解析后,直接在执行点使用这些值。绑定参数不需要转义,因为它们永远不会直接替换到查询字符串中"

这让我觉得,危险是通过替代处理来避免的,而不是通过消除。

这意味着,一个大型项目,其预处理语句转换不完整,组织中不同部分的遗留代码,或者相互通信的服务器,都可能将来自免疫位置或情况的坏消息传递到非免疫位置或情况。

只要清理工作能够胜任地执行,不会造成额外的风险,那么我个人会坚持使用某些层的清理工作,然后调用预处理语句。
-7
Aljo
6 年前
@feedr
我详细说明了他的注释,如下所示
$string = "asda\0sd\x1aas\\\\\\\\dasd\'asdasd\na\'\'sdasdad";
$array1 = array('\\\\\\\\', '\0', '\n', '\r', "'", '"', '\x1a');
$array2 = array('\\\\\\\\\\\\\\\\', '\\\0', '\\\n', '\\\r', "\\\'", '\\\"', '\\\Z');
echo($string);
echo(PHP_EOL);
for( $i=0; $i<count($array1); $i++ ) {
if ($i==0)
$p = '/(?<!\\\\)'.$array1[$i].'(?!\\\\)/';
else
$p = '/(?<!\\\\)'.$array1[$i].'/';
echo($i);
echo($p);
echo( $array2[$i]);
$string = preg_replace($p, $array2[$i], $string);
echo("\t");
echo($string);
echo(PHP_EOL);
}
echo(PHP_EOL);
echo($string);
-32
presto dot dk at gmail dot com
14 年前
如果您要确保您用来执行查询的 ID 是一个数字,请使用 sprint() 或 (int) 或 intval(),但不要使用 mysql_real_escape_string。

ISO-8859-1 的数字 10 与 UTF-8 的数字 10 之间没有区别。
To Top