如果您使用 prepare() 和 bind_param() 作为将任意字符串值放入 SQL 语句的替代方法,则可以避免所有字符转义问题(在 PHP 方面)。这是因为绑定参数值不是通过 SQL 语句语法传递的。
(PHP 5, PHP 7, PHP 8)
mysqli::real_escape_string -- mysqli_real_escape_string — 转义字符串中的特殊字符以用于 SQL 语句,并考虑连接的当前字符集
面向对象风格
过程式风格
此函数用于创建可以在 SQL 语句中使用的合法 SQL 字符串。给定字符串被编码以生成转义的 SQL 字符串,并考虑连接的当前字符集。
字符集必须在服务器级别设置,或者使用 API 函数 mysqli_set_charset() 设置,才能影响 mysqli_real_escape_string()。有关更多信息,请参见关于 字符集 的概念部分。
mysql
仅限过程式风格:由 mysqli_connect() 或 mysqli_init() 返回的 mysqli 对象
string
要转义的字符串。
编码的字符是 NUL (ASCII 0)
、\n
、\r
、\
、'
、"
和 CTRL+Z。
返回转义的字符串。
示例 #1 mysqli::real_escape_string() 示例
面向对象风格
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
$city = "'s-Hertogenbosch";
/* 此查询使用转义的 $city 将正常工作 */
$query = sprintf("SELECT CountryCode FROM City WHERE name='%s'",
$mysqli->real_escape_string($city));
$result = $mysqli->query($query);
printf("Select 返回 %d 行。\n", $result->num_rows);
/* 此查询将失败,因为我们没有转义 $city */
$query = sprintf("SELECT CountryCode FROM City WHERE name='%s'", $city);
$result = $mysqli->query($query);
过程式风格
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = mysqli_connect("localhost", "my_user", "my_password", "world");
$city = "'s-Hertogenbosch";
/* 此查询使用转义的 $city 将正常工作 */
$query = sprintf("SELECT CountryCode FROM City WHERE name='%s'",
mysqli_real_escape_string($mysqli, $city));
$result = mysqli_query($mysqli, $query);
printf("Select 返回 %d 行。\n", mysqli_num_rows($result));
/* 此查询将失败,因为我们没有转义 $city */
$query = sprintf("SELECT CountryCode FROM City WHERE name='%s'", $city);
$result = mysqli_query($mysqli, $query);
上面的示例将输出类似于以下内容
Select returned 1 rows. Fatal error: Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's-Hertogenbosch'' at line 1 in...
如果您使用 prepare() 和 bind_param() 作为将任意字符串值放入 SQL 语句的替代方法,则可以避免所有字符转义问题(在 PHP 方面)。这是因为绑定参数值不是通过 SQL 语句语法传递的。
请注意,此函数不会转义 _(下划线)和 %(百分号),它们在 LIKE 子句中具有特殊含义。
据我所知,没有函数可以做到这一点,因此您必须自己转义它们,在它们前面添加反斜杠。
介绍几个支持 UTF-8/多字节的转义函数。
只要您的数据库连接和多字节扩展使用相同的字符集(UTF-8),这些函数就代表了 mysqli::real_escape_string 的替代方案,它们将通过转义与 mysqli::real_escape_string 相同的字符来产生相同的结果。
这是基于我为我的 SQL 查询构建器类所做的研究
https://github.com/twister-php/sql
<?php
/**
* 返回一个字符串,其中在需要转义的字符之前添加反斜杠。
* 符合 MySQL 的要求,适合多字节字符集
* 编码的字符是 NUL(ASCII 0)、\n、\r、\、'、" 和 ctrl-Z。
*
* @param string $string 要添加反斜杠的字符串
* @return $string 在保留字符前添加 `\`
*
* @author Trevor Herselman
*/
if (function_exists('mb_ereg_replace'))
{
function mb_escape(string $string)
{
return mb_ereg_replace('[\x00\x0A\x0D\x1A\x22\x27\x5C]', '\\\0', $string);
}
} else {
function mb_escape(string $string)
{
return preg_replace('~[\x00\x0A\x0D\x1A\x22\x27\x5C]~u', '\\\$0', $string);
}
}
?>
转义的字符(与 mysqli::real_escape_string 相同)
00 = \0 (NUL)
0A = \n
0D = \r
1A = ctl-Z
22 = "
27 = '
5C = \
注意:preg_replace() 处于 PCRE_UTF8(UTF-8)模式 (`u`)。
增强版
在为 `LIKE` 语法转义字符串时,请记住还需要转义特殊字符 _ 和 %
所以这是一个更安全的版本(即使与 mysqli::real_escape_string 相比,因为用户输入中的 % 字符会导致意外结果,甚至会导致通过 LIKE 语句进行 SQL 注入的安全性问题)
<?php
/**
* 返回一个字符串,其中在需要转义的字符之前添加反斜杠。
* 符合 MySQL 的要求,适合多字节字符集
* 编码的字符是 NUL(ASCII 0)、\n、\r、\、'、" 和 ctrl-Z。
* 此外,还会转义特殊控制字符 % 和 _,
* 适合所有语句,但特别适合 `LIKE`。
*
* @param string $string 要添加反斜杠的字符串
* @return $string 在保留字符前添加 `\`
*
* @author Trevor Herselman
*/
if (function_exists('mb_ereg_replace'))
{
function mb_escape(string $string)
{
return mb_ereg_replace('[\x00\x0A\x0D\x1A\x22\x25\x27\x5C\x5F]', '\\\0', $string);
}
} else {
function mb_escape(string $string)
{
return preg_replace('~[\x00\x0A\x0D\x1A\x22\x25\x27\x5C\x5F]~u', '\\\$0', $string);
}
}
?>
其他转义的字符
25 = %
5F = _
附加函数
原始的 MySQL `utf8` 字符集(用于表和字段)只支持 3 字节序列。
4 字节字符并不常见,但我遇到过 4 字节 UTF-8 字符无法执行查询的情况,因此应尽可能使用 `utf8mb4`。
但是,如果您仍然想使用 `utf8`,可以使用以下函数来替换所有 4 字节序列。
<?php
// 修改自:https://stackoverflow.com/a/24672780/2726557
function mysql_utf8_sanitizer(string $str)
{
return preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $str);
}
?>
选择你的毒药,后果自负!
如果你想知道为什么(除了 \、' 和 ")NUL(ASCII 0)、\n、\r 和 Control-Z 会被转义:这并不是为了防止 SQL 注入,而是为了防止你的 SQL 日志文件变得不可读。
注意在转义 % 和 _ 通配符时要谨慎。根据
https://dev.mysqlserver.cn/doc/refman/5.7/en/string-literals.html#character-escape-sequences
底部的一条经常被忽略的说明,转义序列 \% 和 \_ 只会在 LIKE 中被解释为 % 和 _! (MySQL 8.0 也是如此)
在普通的字符串文字中,转义序列 \% 和 \_ 被视为这两个字符对。因此,如果这些转义序列出现在 WHERE "=" 中而不是 WHERE LIKE 中,它们将不会匹配单个 % 或 _ 字符!
因此,必须使用两个“转义”函数:一个用于普通字符串文字的 real-escape-string(或等效函数),另一个用于专门用于 LIKE 的字符串文字的修改后的转义函数。
注意,“like” 运算符要求对其特殊字符进行 *额外* 级别转义,*在* mysql_escape_string 执行的转义*之上*。但是,没有内置函数来执行此转义。这是一个执行此操作的函数
function escape_sql_wild($s)
/* 转义 s 中的 SQL 模式通配符。 */
{
$result = array();
foreach(str_split($s) as $ch)
{
if ($ch == "\\" || $ch == "%" || $ch == "_")
{
$result[] = "\\";
} /*if*/
$result[] = $ch;
} /*foreach*/
return
implode("", $result);
} /*escape_sql_wild*/