什么是用于 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 正则表达式分隔符?的主要内容,如果未能解决你的问题,请参考以下文章

HTML5都有哪些新的表单属性

HTML5都有哪些新的表单属性

HTML5开发技术试题答案

查找 HTML5 表单用于验证的正则表达式

html5新增的全局属性有哪几个?描述其主要功能。

span标签和p标签有啥区别?啥时候用span