【问题标题】:PHP Intl SpoofChecker::isSuspicious() yields false positivePHP Intl SpoofChecker::isSuspicious() 产生误报
【发布时间】:2018-09-16 04:22:24
【问题描述】:

案例

似乎来自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 class 提到如果使用任何非英文字符,Spoofchecker::isSuspicious() 的返回码将是TRUE,这里不是这种情况。

可能的原因

documentation of Spoofchecker::setAllowedLocales() 目前几乎不存在,参数列表不包含可能值的列表。只能假设它必须与Locale 的兼容。文档内容如下:

区域设置使用 RFC 4646 语言标签(使用连字符,而不是下划线)标识

与Locale 使用下划线作为默认语言环境而不是连字符的测试结果相矛盾。但是当使用$oSpoofchecker-&gt;setAllowedLocales('en-US'); 运行另一个测试时,结果保持不变。

问题

如何正确使用Spoofchecker::isSuspicious()?

【问题讨论】:

  • 如果您的文件编码是 UTF-8,为什么是 'abc' US-ASCII?你确定它是真的 UTF-8
  • US-ASCII 是 UTF-8 的子集。为了仔细检查,我用$sText = chr(97) . chr(98) . chr(99); 替换了$sText = 'abc';,不幸的是这并没有改变结果。我用 UTF-8 字符串在视觉上仔细检查了两者,并检查了 Eclipse 的设置。

标签: php intl


【解决方案1】:

PHP 的 Intl 扩展只是对 ICU 的封装,从 ICU 版本 58 开始,其 Spoofchecker 的误报率有所降低。

来自他们的bug tracker:

ICU 58 反映了最新的 Unicode 更新,它弃用了 Whole-Script Confusables (WSC) 检查和 Mixed-Script Confusables (MSC) 检查,可在 ​http://www.unicode.org/L2/L2016/16229-revising-uts-39-algorithm.pdf.

在 ICU 57 下,检查(WSC 和 MSC)有以下陷阱:

  1. 他们没有将自己限制在由 SpoofChecker#setAllowedChars 或 SpoofChecker#setAllowedLocales。
  2. 他们没有正确处理包含多个骨架的混淆 字符,例如 'æ' 到 'ae'。
  3. WSC 表现出高假阳性 率,尤其是随着越来越多的条目被添加到 confusables.txt。
  4. 所有未通过 MSC 的字符串也未通过限制级别。 (您的字符串“goօgle”就是一个例子。)

考虑到这些陷阱, WSC 和 MSC 从 ICU 58 中移除。

强调我的。 WSC 检查是您的字符串failing。 (请注意,在 ICU 版本为 58.1 及更高版本的情况下,该检查已被完全删除。)

关于如何正确使用Spoofchecker::isSuspicious():

  1. 升级 ICU(这通常是个好主意)或
  2. 按照Syscall's answer 中的说明使用Spoofchecker::setChecks(),并省略WSC 检查Spoofchecker::WHOLE_SCRIPT_CONFUSABLE(涵盖这种情况)和MSC 检查Spoofchecker::MIXED_SCRIPT_CONFUSABLE(同样从最新版本中删除。)

【讨论】:

    【解决方案2】:

    您可以使用Spoofchecker::setChecks(int $checks) 指定如何验证字符串。

    $checks 常量列在 Spoofchecker 类 documentation 中,并由用户 in comments 描述。

    您可以使用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() documentation中的示例,文本Рaypal.com(首字母来自Cyrylic),返回的方法:

    Text "Рaypal.com" IS suspicious 
    

    【讨论】:

      猜你喜欢
      • 2013-07-01
      • 2018-11-09
      • 2019-12-09
      • 1970-01-01
      • 2011-09-05
      • 2014-12-27
      • 2013-05-13
      • 2017-12-22
      • 1970-01-01
      相关资源
      最近更新 更多