Perl:同形异义词攻击。可以比较视觉上相似的 ascii / 非 ascii 字符串吗?
Perl: Homograph attacks. It is possible to compare ascii / non-ascii strings, visually similar?
我遇到了这个所谓的 "homograph attack",我想拒绝解码 punycode 视觉上似乎只是字母数字的域。例如,www.xn--80ak6aa92e.com 将在浏览器(Firefox)中显示 www.apple.com。域在视觉上是相同的,但字符集不同。 Chrome 已经修补了这个并且浏览器显示了 punycode。
我有下面的例子。
#!/usr/bin/perl
use strict;
use warnings;
use Net::IDN::Encode ':all';
use utf8;
my $testdomain = "www.xn--80ak6aa92e.com";
my $IDN = domain_to_unicode($testdomain);
my $visual_result_ascii = "www.apple.com";
print "S1: $IDN\n";
print "S2: $visual_result_ascii";
print "MATCH" if ($IDN eq $visual_result_ascii);
外观相同,但不匹配。可以将 unicode 字符串 ($IDN) 与字母数字字符串进行比较,在视觉上是否相同?
您的示例由 Punycode converter 转换为此 UTF-8 字符串:
www.аррӏе.com
$ perl -e 'printf("%02x ", ord) for split("", "www.аррӏе.com"); print "\n"'
77 77 77 2e d0 b0 d1 80 d1 80 d3 8f d0 b5 2e 63 6f 6d
作为 Unicode:
$ perl -Mutf8 -e 'printf("%04x ", ord) for split("", "www.аррӏе.com"); print "\n"'
0077 0077 0077 002e 0430 0440 0440 04cf 0435 002e 0063 006f 006d
使用@ikegamis 输入:
$ perl -Mutf8 -MEncode -e 'print encode("UTF-8", $_) for ("www.аррӏе.com" =~ /\p{Cyrillic}/g); print "\n"'
аррӏе
$ perl -Mutf8 -MEncode -e 'print encode("UTF-8", $_) for ("www.аррӏе.com" =~ /\P{Cyrillic}/g); print "\n"'
www..com
最初的想法
我不确定是否存在这方面的代码,但我的第一个想法是创建一个地图 \N{xxxx}
-> "visual equivalent ASCII/UTF-8 code"。然后,您可以将 Unicode 字符串上的映射应用于 "convert",将其应用于 ASCII/UTF-8 代码,并将生成的字符串与域列表进行比较。
示例代码(我跳过了 IDN 解码部分,直接在测试数据中使用 UTF-8 结果)。这可能仍然可以改进,但至少它展示了这个想法。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
# Unicode (in HEX) -> visually equal ASCII/ISO-8859-1/... character
my %unicode_to_equivalent = (
'0430' => 'a',
'0435' => 'e',
'04CF' => 'l',
'0440' => 'p',
);
while (<DATA>) {
chomp;
# assuming that this returns a valid Perl UTF-8 string
#my $IDN = domain_to_unicode($_);
my($IDN, $compare) = split(' ', $_) ; # already decoded in test data
my $visually_decoded =
join('', # merge result
map { # map, if mapping exists
$unicode_to_equivalent{sprintf("%04X", ord($_))} // $_
}
split ('', $IDN) # split to characters
);
print "Testing: ", encode('UTF-8', $IDN), " -> $compare ";
print "Visual match!"
if ($visually_decoded eq $compare);
print "\n";
}
exit 0;
__DATA__
www.аррӏе.com www.apple.com
测试运行(取决于从答案中复制和粘贴是否保留了原始的 UTF-8 字符串)
$ perl dummy.pl
Testing: www.аррӏе.com -> www.apple.com Visual match!
计算字符串中脚本的数量
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use Unicode::UCD qw(charscript);
while (<DATA>) {
chomp;
# assuming that this returns a valid Perl UTF-8 string
#my $IDN = domain_to_unicode($_);
my($IDN) = $_; # already decoded in test data
# Unicod characters
my @characters = split ('', $IDN);
# See UTR #39: Unicode Security Mechanisms
my %scripts =
map { (charscript(ord), 1) } # Codepoint to script
@characters;
delete %scripts{Common};
print 'Testing: ',
encode('UTF-8', $IDN),
' (', join(' ', map { sprintf("%04X", ord) } @characters), ')',
(keys %scripts == 1) ? ' not' : '', " suspicious\n";
}
exit 0;
__DATA__
www.аррӏе.com
www.apple.com
www.école.fr
测试运行(取决于从答案中复制和粘贴是否保留了原始的 UTF-8 字符串)
$ perl dummy.pl
Testing: www.аррӏе.com (0077 0077 0077 002E 0430 0440 0440 04CF 0435 002E 0063 006F 006D) suspicious
Testing: www.apple.com (0077 0077 0077 002E 0061 0070 0070 006C 0065 002E 0063 006F 006D) not suspicious
Testing: www.école.fr (0077 0077 0077 002E 00E9 0063 006F 006C 0065 002E 0066 0072) not suspicious
经过一些研究并感谢您的评论,我现在有了结论。
最常见的问题来自西里尔文。此集合包含许多视觉上类似于拉丁字符的字符,您可以进行多种组合。
我发现了一些欺诈性 IDN 域,包括这些名称:
"аррӏе" "сһаѕе" "сіѕсо"
也许在这里,用这个字体,你能看出区别,但在浏览器中是绝对没有视觉区别的。
咨询 https://en.wikipedia.org/wiki/Cyrillic_script_in_Unicode 我能够创建一个 table 具有 12 个外观相似的字符。
更新:我在西里尔字符集中又发现了4个类拉丁字符,现在总共16个。
可以在它们之间创建许多组合,以创建与合法域在视觉上 100% 相似的 IDN。
0430 a CYRILLIC SMALL LETTER A
0441 c CYRILLIC SMALL LETTER ES
0501 d CYRILLIC SMALL LETTER KOMI DE
0435 e CYRILLIC SMALL LETTER IE
04bb h CYRILLIC SMALL LETTER SHHA
0456 i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
0458 j CYRILLIC SMALL LETTER JE
043a k CYRILLIC SMALL LETTER KA
04cf l CYRILLIC SMALL LETTER PALOCHKA
043e o CYRILLIC SMALL LETTER O
0440 p CYRILLIC SMALL LETTER ER
051b q CYRILLIC SMALL LETTER QA
0455 s CYRILLIC SMALL LETTER DZE
051d w CYRILLIC SMALL LETTER WE
0445 x CYRILLIC SMALL LETTER HA
0443 y CYRILLIC SMALL LETTER U
二级域出现问题。扩展名也可以是 IDN,但它们是经过验证的,不能被欺骗并且不是这个问题的主题。
域名注册商将检查所有字母是否来自同一组。如果您混合使用拉丁字符和非拉丁字符,IDN 将不被接受。所以,额外的验证是没有意义的。
我的想法很简单。我们拆分域并仅解码 SLD 部分,然后我们与视觉上相似的西里尔字母列表进行匹配。
如果所有字母在视觉上都与拉丁字母相似,那么结果几乎可以肯定是骗局。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use open ':std', ':encoding(UTF-8)';
use Net::IDN::Encode ':all';
use Array::Utils qw(:all);
my @latinlike_cyrillics = qw (0430 0441 0501 0435 04bb 0456 0458 043a 04cf 043e 0440 051b 0455 051d 0445 0443);
# maybe you can find better examples
my $domain1 = "www.xn--80ak6aa92e.com";
my $domain2 = "www.xn--d1acpjx3f.xn--p1ai";
test_domain ($domain1);
test_domain ($domain2);
sub test_domain {
my $testdomain = shift;
my ($tLD, $sLD, $topLD) = split(/\./, $testdomain);
my $IDN = domain_to_unicode($sLD);
my @decoded; push (@decoded,sprintf("%04x", ord)) for ( split("", $IDN) );
my @checker = array_minus( @decoded, @latinlike_cyrillics );
if (@checker){print "$testdomain [$IDN] seems to be ok\n"}
else {print "$testdomain [$IDN] is possibly scam\n"}
}
我遇到了这个所谓的 "homograph attack",我想拒绝解码 punycode 视觉上似乎只是字母数字的域。例如,www.xn--80ak6aa92e.com 将在浏览器(Firefox)中显示 www.apple.com。域在视觉上是相同的,但字符集不同。 Chrome 已经修补了这个并且浏览器显示了 punycode。
我有下面的例子。
#!/usr/bin/perl
use strict;
use warnings;
use Net::IDN::Encode ':all';
use utf8;
my $testdomain = "www.xn--80ak6aa92e.com";
my $IDN = domain_to_unicode($testdomain);
my $visual_result_ascii = "www.apple.com";
print "S1: $IDN\n";
print "S2: $visual_result_ascii";
print "MATCH" if ($IDN eq $visual_result_ascii);
外观相同,但不匹配。可以将 unicode 字符串 ($IDN) 与字母数字字符串进行比较,在视觉上是否相同?
您的示例由 Punycode converter 转换为此 UTF-8 字符串:
www.аррӏе.com
$ perl -e 'printf("%02x ", ord) for split("", "www.аррӏе.com"); print "\n"'
77 77 77 2e d0 b0 d1 80 d1 80 d3 8f d0 b5 2e 63 6f 6d
作为 Unicode:
$ perl -Mutf8 -e 'printf("%04x ", ord) for split("", "www.аррӏе.com"); print "\n"'
0077 0077 0077 002e 0430 0440 0440 04cf 0435 002e 0063 006f 006d
使用@ikegamis 输入:
$ perl -Mutf8 -MEncode -e 'print encode("UTF-8", $_) for ("www.аррӏе.com" =~ /\p{Cyrillic}/g); print "\n"'
аррӏе
$ perl -Mutf8 -MEncode -e 'print encode("UTF-8", $_) for ("www.аррӏе.com" =~ /\P{Cyrillic}/g); print "\n"'
www..com
最初的想法
我不确定是否存在这方面的代码,但我的第一个想法是创建一个地图 \N{xxxx}
-> "visual equivalent ASCII/UTF-8 code"。然后,您可以将 Unicode 字符串上的映射应用于 "convert",将其应用于 ASCII/UTF-8 代码,并将生成的字符串与域列表进行比较。
示例代码(我跳过了 IDN 解码部分,直接在测试数据中使用 UTF-8 结果)。这可能仍然可以改进,但至少它展示了这个想法。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
# Unicode (in HEX) -> visually equal ASCII/ISO-8859-1/... character
my %unicode_to_equivalent = (
'0430' => 'a',
'0435' => 'e',
'04CF' => 'l',
'0440' => 'p',
);
while (<DATA>) {
chomp;
# assuming that this returns a valid Perl UTF-8 string
#my $IDN = domain_to_unicode($_);
my($IDN, $compare) = split(' ', $_) ; # already decoded in test data
my $visually_decoded =
join('', # merge result
map { # map, if mapping exists
$unicode_to_equivalent{sprintf("%04X", ord($_))} // $_
}
split ('', $IDN) # split to characters
);
print "Testing: ", encode('UTF-8', $IDN), " -> $compare ";
print "Visual match!"
if ($visually_decoded eq $compare);
print "\n";
}
exit 0;
__DATA__
www.аррӏе.com www.apple.com
测试运行(取决于从答案中复制和粘贴是否保留了原始的 UTF-8 字符串)
$ perl dummy.pl
Testing: www.аррӏе.com -> www.apple.com Visual match!
计算字符串中脚本的数量
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use Unicode::UCD qw(charscript);
while (<DATA>) {
chomp;
# assuming that this returns a valid Perl UTF-8 string
#my $IDN = domain_to_unicode($_);
my($IDN) = $_; # already decoded in test data
# Unicod characters
my @characters = split ('', $IDN);
# See UTR #39: Unicode Security Mechanisms
my %scripts =
map { (charscript(ord), 1) } # Codepoint to script
@characters;
delete %scripts{Common};
print 'Testing: ',
encode('UTF-8', $IDN),
' (', join(' ', map { sprintf("%04X", ord) } @characters), ')',
(keys %scripts == 1) ? ' not' : '', " suspicious\n";
}
exit 0;
__DATA__
www.аррӏе.com
www.apple.com
www.école.fr
测试运行(取决于从答案中复制和粘贴是否保留了原始的 UTF-8 字符串)
$ perl dummy.pl
Testing: www.аррӏе.com (0077 0077 0077 002E 0430 0440 0440 04CF 0435 002E 0063 006F 006D) suspicious
Testing: www.apple.com (0077 0077 0077 002E 0061 0070 0070 006C 0065 002E 0063 006F 006D) not suspicious
Testing: www.école.fr (0077 0077 0077 002E 00E9 0063 006F 006C 0065 002E 0066 0072) not suspicious
经过一些研究并感谢您的评论,我现在有了结论。 最常见的问题来自西里尔文。此集合包含许多视觉上类似于拉丁字符的字符,您可以进行多种组合。
我发现了一些欺诈性 IDN 域,包括这些名称:
"аррӏе" "сһаѕе" "сіѕсо"
也许在这里,用这个字体,你能看出区别,但在浏览器中是绝对没有视觉区别的。
咨询 https://en.wikipedia.org/wiki/Cyrillic_script_in_Unicode 我能够创建一个 table 具有 12 个外观相似的字符。
更新:我在西里尔字符集中又发现了4个类拉丁字符,现在总共16个。
可以在它们之间创建许多组合,以创建与合法域在视觉上 100% 相似的 IDN。
0430 a CYRILLIC SMALL LETTER A
0441 c CYRILLIC SMALL LETTER ES
0501 d CYRILLIC SMALL LETTER KOMI DE
0435 e CYRILLIC SMALL LETTER IE
04bb h CYRILLIC SMALL LETTER SHHA
0456 i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
0458 j CYRILLIC SMALL LETTER JE
043a k CYRILLIC SMALL LETTER KA
04cf l CYRILLIC SMALL LETTER PALOCHKA
043e o CYRILLIC SMALL LETTER O
0440 p CYRILLIC SMALL LETTER ER
051b q CYRILLIC SMALL LETTER QA
0455 s CYRILLIC SMALL LETTER DZE
051d w CYRILLIC SMALL LETTER WE
0445 x CYRILLIC SMALL LETTER HA
0443 y CYRILLIC SMALL LETTER U
二级域出现问题。扩展名也可以是 IDN,但它们是经过验证的,不能被欺骗并且不是这个问题的主题。 域名注册商将检查所有字母是否来自同一组。如果您混合使用拉丁字符和非拉丁字符,IDN 将不被接受。所以,额外的验证是没有意义的。
我的想法很简单。我们拆分域并仅解码 SLD 部分,然后我们与视觉上相似的西里尔字母列表进行匹配。 如果所有字母在视觉上都与拉丁字母相似,那么结果几乎可以肯定是骗局。
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use open ':std', ':encoding(UTF-8)';
use Net::IDN::Encode ':all';
use Array::Utils qw(:all);
my @latinlike_cyrillics = qw (0430 0441 0501 0435 04bb 0456 0458 043a 04cf 043e 0440 051b 0455 051d 0445 0443);
# maybe you can find better examples
my $domain1 = "www.xn--80ak6aa92e.com";
my $domain2 = "www.xn--d1acpjx3f.xn--p1ai";
test_domain ($domain1);
test_domain ($domain2);
sub test_domain {
my $testdomain = shift;
my ($tLD, $sLD, $topLD) = split(/\./, $testdomain);
my $IDN = domain_to_unicode($sLD);
my @decoded; push (@decoded,sprintf("%04x", ord)) for ( split("", $IDN) );
my @checker = array_minus( @decoded, @latinlike_cyrillics );
if (@checker){print "$testdomain [$IDN] seems to be ok\n"}
else {print "$testdomain [$IDN] is possibly scam\n"}
}