Case
似乎Intl
扩展的Spoofchecker
会产生误报:
<?php // 7.0 on linux
// File encoding of this script is UTF-8 (thus without BOM)
$sDefaultLocale = (new Locale)->getDefault();
$oSpoofchecker = new Spoofchecker;
$oSpoofchecker->setAllowedLocales($sDefaultLocale);
$sText = 'abc'; // US-ASCII
header('Content-Type: text/plain');
print
'Default locale: ' . $sDefaultLocale . PHP_EOL
. 'Byte length: ' . strlen($sText) . PHP_EOL // US-ASCII check
. 'Text "' . $sText . '" '
. ($oSpoofchecker->isSuspicious($sText, $sError) ? 'IS' : 'IS NOT')
. ' suspicious' . PHP_EOL
. 'Spoofchecker internal error information:' . PHP_EOL;
var_dump($sError);
结果
Default locale: en_US_POSIX
Byte length: 3
Text "abc" IS suspicious
Spoofchecker internal error information:
NULL
预期成果
Text "abc" IS NOT suspicious
这是因为 abc 是 US-ASCII,它应该是en_US_POSIX
的默认值。PHP Spoofchecker 类还提到,如果使用任何非英文字符,Spoofchecker::isSuspicious()
的返回代码将被TRUE
,但这里的情况并非如此。
可能的原因
Spoofchecker::setAllowedLocales()
的文档目前几乎不存在,参数列表不包含可能值的列表。人们只能假设它必须与Locale
兼容。文档内容如下:
区域设置使用 RFC 4646 语言标记(使用连字符,而不是下划线)进行标识
与测试结果相矛盾,其中Locale
使用下划线作为默认区域设置而不是连字符。但是,当使用$oSpoofchecker->setAllowedLocales('en-US');
运行另一个测试时,结果保持不变。
问题
如何正确使用Spoofchecker::isSuspicious()
?
PHP 的国际扩展只是 ICU 的包装器,其 Spoofchecker 从 ICU 版本 58 开始减少了误报。
从他们的错误跟踪器:
ICU 58 反映了最新的 Unicode 更新,该更新弃用了 全脚本可混淆 (WSC) 检查和混合脚本可混淆项 (MSC) 检查,可在 http://www.unicode.org/L2/L2016/16229-revising-uts-39-algorithm.pdf。
在ICU 57下,检查(WSC和MSC)有以下缺陷:
- 他们没有将自己限制在指定的字符集 SpoofChecker#setAllowedChars 或 SpoofChecker#setAllowedLocales.
- 他们没有正确处理包含多个骨架的混淆对象 字符,如"æ"到"ae"。
- WSC表现出高假阳性 率,特别是随着越来越多的条目被添加到 令人困惑.txt。
- 所有未通过 MSC 的字符串也未达到限制级别。 (你的字符串"goօgle"就是一个例子。
考虑到这些陷阱, WSC和MSC从ICU 58中移除。
强调我的。WSC 检查是字符串失败的原因。(请注意,它在 ICU 版本为 58.1 及更高版本的地方通过,因为该检查已被完全删除。
至于如何正确使用Spoofchecker::isSuspicious():
- 升级 ICU(这通常是个好主意)或
- 按照 Syscall 的答案使用
Spoofchecker::setChecks()
,省略 WSC 检查Spoofchecker::WHOLE_SCRIPT_CONFUSABLE
(涵盖这种情况)和 MSC 检查Spoofchecker::MIXED_SCRIPT_CONFUSABLE
(同样从最新版本中删除)。
您可以使用Spoofchecker::setChecks(int $checks)
来指定如何验证字符串。
$checks
常量列在 Spoofchecker 类文档中,并由用户在注释中描述。
您可以使用SpoofChecker::CHAR_LIMIT
(或多个常量的组合,例如:SpoofChecker::CHAR_LIMIT|Spoofchecker::INVISIBLE
):
CHAR_LIMIT
:检查标识符是否仅包含一组指定的可接受字符中的字符。INVISIBLE
:检查标识符是否存在不可见字符(如零宽度空格)或可能不会显示的字符序列,例如多次出现相同的非空格标记。
$sDefaultLocale = (new Locale)->getDefault();
$oSpoofchecker = new Spoofchecker;
$oSpoofchecker->setAllowedLocales($sDefaultLocale);
$oSpoofchecker->setChecks(SpoofChecker::CHAR_LIMIT);
$sText = 'abc'; // US-ASCII
header('Content-Type: text/plain');
print
'Default locale: ' . $sDefaultLocale . PHP_EOL
. 'Byte length: ' . strlen($sText) . PHP_EOL // US-ASCII check
. 'Text "' . $sText . '" '
. ($oSpoofchecker->isSuspicious($sText, $sError) ? 'IS' : 'IS NOT')
. ' suspicious' . PHP_EOL
. 'Spoofchecker internal error information:' . PHP_EOL;
var_dump($sError);
将输出:
Default locale: en_US_POSIX
Byte length: 3
Text "abc" IS NOT suspicious
Spoofchecker internal error information:
NULL
使用isSuspicious()
文档中的示例,文本Рaypal.com
(第一个字母来自 Cyrylic),返回的方法:
Text "Рaypal.com" IS suspicious