逃离MySQL通配符

Col*_*mbo 38 php mysql escaping

在我使用的旧服务器上,我无法使用预准备语句,我目前正在尝试完全转义用户输入,然后再将其发送到MySQL.为此我使用PHP函数mysql_real_escape_string.

由于这个函数没有逃脱MySQL通配符%和_我也用它addcslashes来逃避这些.

当我发送类似的东西:

test_test " ' 
Run Code Online (Sandbox Code Playgroud)

到数据库然后读回数据库显示:

test\_test " ' 
Run Code Online (Sandbox Code Playgroud)

看着这个我无法理解为什么_有一个前面的反斜杠但是'和'没有.因为它们全部用\ _确定_'并且"应该都显示相同,即所有都具有转义字符可见或一切都没有可见.

是否会自动筛选转义

有谁能解释一下?

bob*_*nce 89

_并且%通常不是MySQL中的通配符,并且不应为了将它们放入普通的字符串文字而进行转义.mysql_real_escape_string为此目的是正确和充分的.addcslashes不应该使用.

_并且%仅在LIKE匹配的背景下是特殊的.当你想在一个LIKE语句中为字面意思使用字符串时,所以100%匹配百分之百而不是只有一百个开头的字符串,你有两个逃避级别需要担心.

第一个是LIKE逃避.LIKE处理完全在SQL内部进行,如果要将文字字符串转换为文字LIKE表达式,即使使用参数化查询,也必须执行此步骤!

在这个计划中,_并且%是特殊的,必须进行转义.转义字符也必须转义.根据ANSI SQL,不能转义除此之外的字符:\'将是错误的.(虽然MySQL通常会让你逃脱它.)

完成此操作后,您将进入第二级转义,这是简单的旧字符串文字转义.这发生在SQL之外,创建SQL,因此必须在LIKE转义步骤之后完成.对于MySQL,这mysql_real_escape_string和以前一样; 对于其他数据库,将有一个不同的功能,您可以只使用参数化查询,以避免必须这样做.

导致混淆的问题是,在MySQL中使用反斜杠作为两个嵌套转义步骤的转义字符!因此,如果您想将字符串与文字百分号匹配,则必须使用双反斜杠转义并说LIKE 'something\\%'.或者,如果它在PHP "文字中也使用反斜杠转义,"LIKE 'something\\\\%'".哎呀!

根据ANSI SQL,这是不正确的,它表示:在字符串文字中,反斜杠意味着字面反斜杠和逃避单引号的方式是''; 在LIKE表达式中,默认情况下根本没有转义字符.

因此,如果您想以可移植的方式进行LIKE-escape,则应使用该LIKE ... ESCAPE ...构造覆盖默认(错误)行为并指定自己的转义字符.为了理智,我们会选择除了该死的反斜杠之外的东西!

function like($s, $e) {
    return str_replace(array($e, '_', '%'), array($e.$e, $e.'_', $e.'%'), $s);
}

$escapedname= mysql_real_escape_string(like($name, '='));
$query= "... WHERE name LIKE '%$escapedname%' ESCAPE '=' AND ...";
Run Code Online (Sandbox Code Playgroud)

或带参数(例如在PDO中):

$q= $db->prepare("... WHERE name LIKE ? ESCAPE '=' AND ...");
$q->bindValue(1, '%'.like($name, '=').'%', PDO::PARAM_STR);
Run Code Online (Sandbox Code Playgroud)

(如果你想要更多的可移植性派对时间,你也可以尝试考虑MS SQL Server和Sybase,其中[角色在一个LIKE语句中也是错误的,特殊的,并且必须被转义.agh.)

  • 我会再次点击"该死的反斜杠!". (5认同)
  • 当然,但想要能够搜索文字的'%`或`_`字符是很自然的.如果用户在前端搜索"50%",他们可能意味着他们正在寻找一个包含"50%"的字符串而不只是其中包含"50"的任何字符串. (3认同)
  • 在使用例如 utf8mb4 语言特定排序规则时,还要注意 https://bugs.mysql.com/bug.php?id=39808(请参阅此处的列表 https://hastebin.com/acoqedajij)。 (2认同)

use*_*939 6

令人惊讶的是这么多年之后没有人愿意提及它,但如果您不需要进行复杂的通配符匹配(例如foo%baz),我认为INSTR/ LOCATE/ POSITIONLEFTRIGHT等应该足够了。在我的所有情况下,我只用来LIKE匹配字符串中的任何位置(即,例如%foobar%),因此在经历了所有关于转义模式的恐怖故事之后LIKE,我现在正在使用INSTR

等价于value LIKE '%foobar%'(任何地方匹配):

INSTR(value, 'foobar') > 0
Run Code Online (Sandbox Code Playgroud)

相当于value LIKE 'foobar%'(开始时的匹配):

INSTR(value, 'foobar') = 1
Run Code Online (Sandbox Code Playgroud)

等价于value LIKE '%foobar'(结束时匹配):

RIGHT(value, 6) = 'foobar'
Run Code Online (Sandbox Code Playgroud)

它可能不那么直接和容易记住,最后的匹配解决方案也许可以以某种方式改进以更加通用。但这些替代方案至少应该让您在安全性方面感到安心,因为它绕过了任何自滚动转义的需要,并且不需要您更改实际的参数值(无论如何使用准备好的语句时)。