我想验证php中的域url,它可能是国际化的域名格式,如希腊语 域名= http://παράδειγμα.δοκιμή 他们是否可以使用正则表达式验证它?
答案 0 :(得分:3)
如果您想创建自己的图书馆,则需要使用允许的代码点表(IANA — Repository of IDN Practices,IDN Character Validation Guidance,IDNA Parameters)和表格 Unicode脚本属性(UNIDATA/Scripts.txt)。
Gmail采用Unicode Consortium的“H ighly Restricted”规范(Protecting Gmail in a global world)。 允许使用以下Unicode脚本的组合。
您可能需要支付特殊脚本属性值(Common,Inherited,Unknown)的注意权,因为某些字符具有多个属性或错误的属性。
例如,U + 3099(COMKINING KATAKANA-HIRAGANA VOICED SOUND MARK)有两个属性(“Katakana”和“Hiragana”),PCRE功能将其归类为“继承”。另一个例子是U + x2A708。 Al +#2A708(U + 30C8 KATAKANA LETTER TO和U + 30E2 KATAKANA LETTER MO的组合)的右脚本属性是“Katakana”,Unicode规范将其错误分类为“Han”。
您可能需要考虑IDN homograph attack。 Google Chrome浏览器IDN policy采用the blacklist chars。
我的建议是使用Zend \ Validator \ Hostname。该库使用the table of permitted code points表示日语和中文。
如果您使用Symfony,请考虑将版本的应用升级到2.5,它采用egulias / email-validatornd(Manual)。 您需要额外验证字符串是否格式正确。查看我的report a>细节。
不要忘记XSS和SQL注入。以下地址是基于RFC5322的有效电子邮件地址。
// From Japanese tutorial
// http://blog.tokumaru.org/2013/11/xsssqlrfc5322.html
"><script>alert('or/**/1=1#')</script>"@example.jp
我认为使用idn_to_ascii进行验证是值得怀疑的,因为idn_to_ascii几乎传递了所有字符。
for ($i = 0; $i < 0x110000; ++$i) {
$c = utf8_chr($i);
if ($c !== '' && false !== idn_to_ascii($c)) {
$number = strtoupper(dechex($i));
$length = strlen($number);
if ($i < 0x10000) {
$number = str_repeat('0', 4 - $length).$number;
}
$idn = $c.'example.com';
echo 'U+'.$number.' ';
echo ' '.$idn.' '. idn_to_ascii($idn);
echo PHP_EOL;
}
}
function utf8_chr($code_point) {
if ($code_point < 0 || 0x10FFFF < $code_point || (0xD800 <= $code_point && $code_point <= 0xDFFF)) {
return '';
}
if ($code_point < 0x80) {
$hex[0] = $code_point;
$ret = chr($hex[0]);
} else if ($code_point < 0x800) {
$hex[0] = 0x1C0 | $code_point >> 6;
$hex[1] = 0x80 | $code_point & 0x3F;
$ret = chr($hex[0]).chr($hex[1]);
} else if ($code_point < 0x10000) {
$hex[0] = 0xE0 | $code_point >> 12;
$hex[1] = 0x80 | $code_point >> 6 & 0x3F;
$hex[2] = 0x80 | $code_point & 0x3F;
$ret = chr($hex[0]).chr($hex[1]).chr($hex[2]);
} else {
$hex[0] = 0xF0 | $code_point >> 18;
$hex[1] = 0x80 | $code_point >> 12 & 0x3F;
$hex[2] = 0x80 | $code_point >> 6 & 0x3F;
$hex[3] = 0x80 | $code_point & 0x3F;
$ret = chr($hex[0]).chr($hex[1]).chr($hex[2]).chr($hex[3]);
}
return $ret;
}
如果要通过Unicode脚本属性验证域,请使用PCRE函数。
以下代码显示如何获取Unicode脚本属性的名称。如果您想在JavaScript中使用Unicode脚本peroperties,请使用mathiasbynens/unicode-data。
function get_unicode_script_name($c) {
// http://php.net/manual/regexp.reference.unicode.php
$names = [
'Arabic', 'Armenian', 'Avestan', 'Balinese', 'Bamum', 'Batak', 'Bengali',
'Bopomofo', 'Brahmi', 'Braille', 'Buginese', 'Buhid', 'Canadian_Aboriginal',
'Carian', 'Chakma', 'Cham', 'Cherokee', 'Common', 'Coptic', 'Cuneiform',
'Cypriot', 'Cyrillic', 'Deseret', 'Devanagari', 'Egyptian_Hieroglyphs',
'Ethiopic', 'Georgian', 'Glagolitic', 'Gothic', 'Greek', 'Gujarati',
'Gurmukhi', 'Han', 'Hangul', 'Hanunoo', 'Hebrew', 'Hiragana', 'Imperial_Aramaic',
'Inherited', 'Inscriptional_Pahlavi', 'Inscriptional_Parthian', 'Javanese',
'Kaithi', 'Kannada', 'Katakana', 'Kayah_Li', 'Kharoshthi', 'Khmer', 'Lao', 'Latin',
'Lepcha', 'Limbu', 'Linear_B', 'Lisu', 'Lycian', 'Lydian', 'Malayalam', 'Mandaic',
'Meetei_Mayek', 'Meroitic_Cursive', 'Meroitic_Hieroglyphs', 'Miao', 'Mongolian',
'Myanmar', 'New_Tai_Lue', 'Nko', 'Ogham', 'Old_Italic', 'Old_Persian',
'Old_South_Arabian', 'Old_Turkic', 'Ol_Chiki', 'Oriya', 'Osmanya', 'Phags_Pa',
'Phoenician', 'Rejang', 'Runic', 'Samaritan', 'Saurashtra', 'Sharada', 'Shavian',
'Sinhala', 'Sora_Sompeng', 'Sundanese', 'Syloti_Nagri', 'Syriac', 'Tagalog',
'Tagbanwa', 'Tai_Le', 'Tai_Tham', 'Tai_Viet', 'Takri', 'Tamil', 'Telugu', 'Thaana',
'Thai', 'Tibetan', 'Tifinagh', 'Ugaritic', 'Vai', 'Yi'
];
$ret = [];
foreach ($names as $name) {
$pattern = '/\p{'.$name.'}/u';
if (preg_match($pattern, $c)) {
return $name;
}
}
return '';
}
答案 1 :(得分:2)
这是idn个域名,我首先将其转换为puny code版本,然后将validate域转换为。{/ p>
但是,如果您真的想要通过正则表达式验证
<?php
$domain = 'παράδειγμα.gr';
$regex = '#^([\w-]+://?|www[\.])?([^\-\s\,\;\:\+\/\\\?\^\`\=\&\%\"\'\*\#\<\>]*)\.[a-z]{2,7}$#';
if (preg_match($regex, $domain)) {
echo "VALID";
}
但是你让你运行错误的假设,因为验证一个idn域非常复杂,我试图验证没有无效的字符,但列表不完整。
更好地将bevore转换为惩罚代码
$regex = '#^([\w-]+://?|www[\.])?[a-z0-9]+[a-z0-9\-\.]*[a-z0-9]+\.[a-z]{2,7}$#';
if (preg_match($regex, idn_to_ascii($domain))) {
echo "VALID";
}
如果您想要测试域是否可以解析,请尝试:
$regex = '#^([\w-]+://?|www[\.])?[a-z0-9]+[a-z0-9\-\.]*[a-z0-9]+\.[a-z]{2,7}$#';
$punny_domain = idn_to_ascii($domain);
if (preg_match($regex, $punny_domain)) {
if (gethostbyname($punny_domain) != $punny_domain) {
echo "VALID";
}
}
答案 2 :(得分:1)
这是一个所谓的IDN domain。 支持IDN域的客户端使用RFC 5890中指定的IDNA2008标准对其进行规范化,然后在提交DNS解析之前使用Punycode中定义的RFC 3492编码替换剩余的unicode字符。
根据规范,UTF-8字符集中的每个字符都可以在IDN域中使用,但每个顶级域权限都可以在Unicode字符集中定义有效字符,因此很难create and maintain a real regex
如果要在应用程序中接受IDN域,则应在内部使用编码版本。 PHP extension intl带来两个函数来解码和解码IDN域名
echo idn_to_ascii('täst.de');
xn--tst-qla.de
编码后,域名将通过任何traditional regex check
简单验证:
$url = "http://example.com/";
if (preg_match('/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i', $url)) {
echo 'OK';
} else {
echo 'Invalid URL.';
}
修改强>
如果您想要真正的DNS验证,可以使用dns_get_record(PHP 5)或gethostbyaddr
e.g。
$domain = 'ελληνικά.idn.icann.org';
$idnDomain = idn_to_ascii( $domain );
if ( $dnsResult = dns_get_record( $idnDomain, DNS_ANY ) )
{
echo $idnDomain , "\n";
print_r( $dnsResult );
}
else
{
echo "failed to lookup domain\n";
}
结果:
xn--hxargifdar.idn.icann.org
Array
(
[0] => Array
(
[host] => xn--hxargifdar.idn.icann.org
[class] => IN
[ttl] => 21456
[type] => A
[ip] => 199.7.85.10
)
[1] => Array
(
[host] => xn--hxargifdar.idn.icann.org
[class] => IN
[ttl] => 21600
[type] => AAAA
[ipv6] => 2620::2830:230:0:0:0:10
)
)