什么是用于 HTML5 模式输入元素属性的安全 PCRE 正则表达式分隔符?
Posted
技术标签:
【中文标题】什么是用于 HTML5 模式输入元素属性的安全 PCRE 正则表达式分隔符?【英文标题】:What is a safe PCRE regex delimiter to use on HTML5 pattern input element attribute? 【发布时间】:2012-03-20 13:45:59 【问题描述】:似乎html5 spec (and therefore ECMA262) 允许<input type="text" pattern="[0-9]/[0-9]" />
匹配字符串'0/0',即使正斜杠没有转义。像 Drupal 这样的 Web 应用程序希望为不支持 HTML5 的浏览器提供服务器端验证,例如:
<?php
preg_match('/^(' . $pattern . ')$/', $value);
?>
很遗憾,字符串“[0-9]/[0-9]”不是有效的PRCE 正则表达式。似乎大多数(如果不是全部)支持 HTML5 的浏览器都支持 pattern="[0-9]/[0-9]"
和 pattern="[0-9]\/[0-9]"
这引出了一个问题 - 我们可以使用什么作为分隔符来针对 Perl 样式的正则表达式运行此模式?
我们已经提交了bug report against the W3C spec,但这里的浏览器有问题吗?是否需要澄清 HTML5 规范?有没有我们可以在 PHP 中使用的解决方法?
【问题讨论】:
好问题,尽管正如亚历克斯指出的那样,没有解决方案。作为旁注,附加^
和$
字符可能已经破坏了模式,不是吗?考虑$pattern = "(^|foo)bar"
。
pattern 属性的规范说它必须匹配整个字符串,而不仅仅是它的一部分,所以我认为这将是一个无效的模式?
【参考方案1】:
如果您使用#
而不是/
作为分隔符,这是一个有效的正则表达式。示例:
preg_match('#^('.$pattern.')$#', $value);
【讨论】:
看来#
在 $pattern 中使用也是有效的。我可以有pattern="\#[0-9]"
或pattern="#[0-9]"
,它们都会根据浏览器当前实现模式匹配的方式来匹配字符串“#1”。
是的,它是一个可以使用的有效字符,我的意思是您可以将其用作解决方法,因为没有 真正的 解决方案。【参考方案2】:
PCRE 的一个问题是几乎 任何 分隔符对于开始和结束标记都是合法的,这取决于使其余转义更容易的原因。所以#foo# 是合法的,/foo/ 是合法的,!foo!是合法的(我认为)等等。我想说,正是由于这个原因,未定界的正则表达式非常危险。这听起来像是一个没有指定的 HTML5 规范错误。
也许在 PHP 中,扫描字符串并从字符串中不存在的白名单中选择一个分隔符? (例如,如果没有 / 使用那个,如果有使用 #,如果有使用 % 等)
【讨论】:
【参考方案3】:我认为chr(0)
可以正常工作。编辑:没有。但是chr(1)
确实有效。
【讨论】:
chr(1)
是有效的 UTF-8 字符,因此它可能以 HTML 形式出现。不太可能,但可能。我推荐使用"\xFF"
字节,因为它在UTF-8字符串中是不允许的,所以我们可以确定它不会发生。并且因为 preg_match 不理解 UTF-8,所以不会造成任何麻烦。示例:preg_match("\xFF$pattern\$\xFFADmsu", $subject);
(请注意 ADmsu
修饰符并添加 $
。u
修饰符仅在模式中需要有效的 UTF-8 字节,而不是在分隔符周围。)
您绝对应该将此作为答案发布,以便我们投票,Dave 可以接受。【参考方案4】:
鉴于 PHP 应用程序(在本例中为 Drupal)正在生成输入字段,似乎一种解决方法是执行以下操作:
$pattern = '[0-9]/[0-9]';
...
$cleanPattern = preg_replace('/\//', '\\/', $pattern);
preg_match('/' . $cleanPattern . '/', $subject, $matches);
我想不出这不起作用的情况,/
被用作表达式中的文字。
HTML5 规范遵从 ECMA262 的合法模式规范:
如果指定,该属性的值必须与 javascript 模式生成相匹配。 [ECMA262]
由于在 ECMA262 中定义了 BNF,因此完整的解析器(而不是使用 PCRE)似乎是最安全的方法。
【讨论】:
'\\/'
应该与'\/'
匹配吗?您的代码会将其转换为与'/'
匹配的'\/'
,但与预期的'\/'
不匹配。
我不认为是这样,但是在 shell 中进行测试是令人沮丧的。 HTML5 模式值\\/
与在输入字段中输入的不带引号的\/
匹配,PCRE /^\\\/$/
可以匹配。作为测试说$pattern = '\\\\/';
(两个转义的反斜杠,后跟一个转义的斜杠)。以上pcre_replace
使$cleanPattern
评估为\\\/
,据我所知,它可以匹配。
由于匹配的简单性,这也应该是 str_replace
而不是 preg_replace
。【参考方案5】:
我推荐使用"\xFF"
字节作为模式分隔符,因为它在UTF-8字符串中是不允许的,所以我们可以确定它不会出现在模式中。并且因为 preg_match 不理解 UTF-8,所以不会造成任何麻烦。
示例:preg_match("\xFF$pattern\$\xFFADmsu", $subject);
请注意ADmsu
修饰符并添加$
。 u
修饰符仅在模式中需要有效的 UTF-8 字节,而不是在周围的分隔符中。
【讨论】:
最新的 Firefox 和 Chrome 都允许\xFF
模式匹配文本输入字段中的文字 0xFF 字节。
opello:我刚试了一下,没用。我在输入字段中添加了pattern="a\xFFb"
属性,然后输入了这个字节序列(从 hexeditor 复制),Chromium 说它不匹配。我希望这是正确的行为,因为它不是有效的 UTF-8 字符串。但是,我没有在非 UTF-8 页面上尝试过。
我模拟了我在这里测试过的东西:jsfiddle.net/4nngJ,它仍然与我在 Chrome 和 Firefox 中看到的结果相同。
我的 Firefox 不喜欢即使选择文本 :) 这里是 updated version 包含应该匹配的值。我看到红色的输入。你?
我也看到了。仔细检查后,您指定的值实际上是 3 个字节,0xEF 0xBF 0xBD
,它实际上对应于 Unicode 替换字符代码点,而不是 \xFF,应该会失败。请参阅this further updated version,它的值中有一个0xFF
字节。 更新: 如果您在早期版本中使用 \uFFFD 模式,它确实有效。【参考方案6】:
只需将其括在方括号或圆括号中(是的,这很奇怪!):
<?php
preg_match('(^' . $pattern . '$)', $value);
?>
手册说明你可以使用所有对应的对:http://php.net/manual/en/regexp.reference.delimiters.php
一开始并不容易,但它清楚地处理了您可能在两者之间使用的任何字符。例如,'(^(foo|bar)$)'
作为最终的正则表达式:^(foo|bar)$
,没有任何潜在的危险转义。
【讨论】:
【参考方案7】:您也可以使用 T-Regx 并让它相应地选择分隔符:
<?php
pattern("^($pattern)$")->match($value);
它会添加模式中未使用的任何分隔符。
【讨论】:
以上是关于什么是用于 HTML5 模式输入元素属性的安全 PCRE 正则表达式分隔符?的主要内容,如果未能解决你的问题,请参考以下文章