完全限定域名验证

39
有没有一种快速且简便的方法来验证是否输入了正确的FQDN?请记住,没有DNS服务器或Internet连接,所以必须通过regex/awk/sed进行验证。
有什么想法吗?

不完全是这样。至少,它不会很可靠。你可以通过保持自己的顶级域名列表(需要及时更新)来检查TLD部分是否有效,但除此之外,我想你就没什么办法了 :) - favoretti
1
试试这个,它是一个正则表达式: https://dev59.com/VlTTa4cB1Zd3GeqPoxGl - tombolinux
我的想法是验证用户输入的DNS名称是否符合标准,例如groupa-zone1appserver.example.com。 - Riaan
http://www.ietf.org/rfc/rfc2181.txt 第11节。它们不必是ASCII码。 - pizza
6个回答

67
(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)

正则表达式对于这种情况,最多只能是近似值,并且规则会随时间而改变。上面的正则表达式是针对以下情况编写的,特定于主机名-

主机名由一系列标签连接在一起组成。每个标签长度为1到63个字符,可以包含:

  • ASCII字母a-z(不区分大小写),
  • 数字0-9,
  • 连字符('-')。

此外:

一些假设:

  • TLD至少为2个字符且只包含a-z
  • 我们希望至少有1个TLD以上的级别

结果:有效/无效

  • 911.gov - 有效
  • 911 - 无效(无TLD)
  • a-.com - 无效
  • -a.com - 无效
  • a.com - 有效
  • a.66 - 无效
  • my_host.com - 无效(下划线)
  • typical-hostname33.whatever.co.uk - 有效

编辑: John Rix提供了另一种正则表达式的技巧,使TLD的规定变成可选的:

(?=^.{1,253}$)(^(((?!-)[a-zA-Z0-9-]{1,63}(?<!-))|((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63})$)
  • 911 - 有效
  • 911.gov - 有效

编辑2: 有人要求js版本。 它在js中无法工作的原因是因为js不支持正则表达式反向查找。 具体来说,代码(?<!-)指定了前一个字符不能是连字符。

无论如何,这里重写了没有反向查找的代码——有点丑但不多。

(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)

你也可以在John Rix的版本上进行相似的替换。

编辑3:如果你想允许尾点——这在技术上是被允许的:

(?=^.{4,253}\.?$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}\.?$)

在 @ChaimKut 指出之前,我不熟悉尾点语法,并进行了一些研究。

然而,在我使用的各种工具中,使用尾点似乎会导致一些不太可预测的结果,因此建议谨慎使用。


1
这里有一个(有点hacky的)替代版本,可以验证没有关联域名的主机名。有什么改进吗? (?=^.{1,254}$)(^(((?!-)[a-zA-Z0-9-]{1,63}(?<!-))|((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63})$) - John Rix
1
有人能提供这个正则表达式的Javascript版本吗? - T Nguyen
1
您需要允许尾随点。请参阅http://en.wikipedia.org/wiki/Fully_qualified_domain_name - ChaimKut
1
嗯,你在技术上是正确的。我也了解到,在不计算尾部句点的情况下,你只能使用253个ASCII字符。 - bkr
1
这并没有考虑顶级域名中的punycode,并将任何尾随点与253限制一起计算。 - Martijn
显示剩余8条评论

20

如今由于国际化域名和数千个新的顶级域名,这变得更加困难。

简单的部分是您仍然可以在“.”上拆分组件。

您需要一个可注册的顶级域名列表。有一个网站可以提供:

https://publicsuffix.org/list/effective_tld_names.dat

您只需要检查ICANN认可的那些。请注意,可注册的顶级域名可以有多个组件,例如“co.uk”。

然后是IDN和punycode。现在域名是Unicode编码的。例如,

“xn--nnx388a”相当于“臺灣”。顺便说一下,这两者都是有效的顶级域名。

有关punycode转换代码,请参见“http://golang.org/src/pkg/net/http/cookiejar/punycode.go”。。

每个域名组件语法的检查也有了新规则。请参见RFC5890: https://www.rfc-editor.org/rfc/rfc5890

组件可以是A-labels(仅ASCII)或Unicode。 ASCII标签要么遵循旧语法,要么以“xn--”开头,在这种情况下,它们是Unicode字符串的punycode版本。

Unicode的规则非常复杂,详见RFC5890。这些规则旨在防止从左到右和从右到左集合中混合字符等问题。

很抱歉没有简单的答案。


1
如果验证应该在任何网络上工作,请不要假设FQDN必须以官方TLD结尾。内部网络可能具有任何TLD,只要它在内部解析即可。一个经典的例子是.company内部TLD。 - Marcos Dione

8
这是您需要的正则表达式:

这个正则表达式就是您要找的:

(?=^.{1,254}$)(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)

它匹配你的示例域名(groupa-zone1appserver.example.com或cod.eu等...)

我尝试解释一下:

(?=^.{1,254}$) 匹配长为1到254个字符的域名(可以以任何字符开头),如果我们假设co.uk是最小长度,则还可以是5,254。

(^ 开始匹配

(?: 定义一个匹配组

(?!\d+\.) 域名不应由数字组成,因此不接受1234.co.uk或abc.123.uk,而接受1a.ko.uk。

[a-zA-Z0-9_\-] 域名应由只包含a-zA-Z0-9_-的单词组成

{1,63} 任何域级别的长度最多为63个字符(也可以是2,63)

+

(?:[a-zA-Z]{2,})$) 域名的最后一部分不应跟随任何其他单词,并且必须由至少两个a-zA-Z字符组成的单词组成。


1
你想解释一下这个符号吗?它对 ac.uk 做了什么?那不是一个有效的 FQDN;它是国家代码顶级域名下的中间级别域名。 - Jonathan Leffler
使用正则表达式,你只能匹配语法,而不能匹配真实的DNS完全限定域名。 - tombolinux
2
?:(?!\d+\.) 这段代码不应该存在,因为只有数字的域名仍然是有效的,比如 911.com。 - Unixmonkey
1
@Unixmonkey - 你是对的,有很多有效的仅数字子域名。 - bkr
888.com不符合此正则表达式。 - Nati
显示剩余3条评论

4

我们使用此正则表达式来验证在实际应用中出现的域名,它涵盖了我知道的所有实际情况,新的情况也欢迎添加。根据我们的准则,它避免了非捕获组和贪婪匹配。

^(?!.*?_.*?)(?!(?:[\w]+?\.)?\-[\w\.\-]*?)(?![\w]+?\-\.(?:[\w\.\-]+?))(?=[\w])(?=[\w\.\-]*?\.+[\w\.\-]*?)(?![\w\.\-]{254})(?!(?:\.?[\w\-\.]*?[\w\-]{64,}\.)+?)[\w\.\-]+?(?<![\w\-\.]*?\.[\d]+?)(?<=[\w\-]{2,})(?<![\w\-]{25})$

证明和解释:https://regex101.com/r/FLA9Bv/40

验证域名时有两种方法可供选择。

按照规范的FQDN匹配(理论定义,在实践中很少遇到):

实用/保守的FQDN匹配(实际定义,预期并且在实践中受到支持):

  • 按照规范进行匹配,但以下情况除外/添加
  • 有效字符:[a-zA-Z0-9.-]
  • 标签不能以连字符开头或结尾(根据RFC-952RFC-1123/2.1
  • TLD最小长度为2个字符,最大长度为24个字符,根据目前存在的记录
  • 不匹配尾随点

上述正则表达式包含了符合规范和实用规则。


请注意,“任何字符都允许”的规则适用于DNS中的标签。但是,对于有效主机名有一些限制(RFC1123)。原则上可以创建将IP地址映射到二进制x86代码的PTR记录,但我不建议让任何人通过API或表单字段填写此类记录,因此应该遵守RFC1123的限制。 - Steven
2
每个\w\d\d\w都应该被替换为仅\w,它是\d的适当超集。 - AndrewF
如果你允许任何字符,那么你可能会遇到 "; DROP * 或其他有趣的 DNS 标签或值。假设你只处理主机/域名,RFC1123 限制了允许的字符集。注意,这也意味着 _ 不被允许。所以 this-is-a-host.example.com 是可以的,而 this_is_a_host.example.com 不行; -this-is-a-host-.example.com 也一样不行。 - Steven
1
@Steven 正则表达式中不允许使用这些字符 (_ * ;). 如前所述,它包含实际规则。我建议你尝试一下,如果你发现有什么应该或不应该被允许的,请让我们讨论一下。 - thisismydesign
1
@Steven,你可以通过regex101链接尝试一下。并且不允许使用下划线。有效字符列表在答案中。正则表达式比起初看来要复杂一些,所以正如我之前提到的,你应该先尝试一下。 - thisismydesign
显示剩余3条评论

3

注意事项 #1:

请注意,由于 RFC-2181 中对 DNS 标签的要求放宽,标签可以包含几乎任何组合的符号(但长度限制仍然存在):

"任何二进制字符串都可以用作任何资源记录的标签。DNS 协议的实现不得对可以使用的标签施加任何限制。特别地,DNS 服务器不得拒绝服务区域,因为它包含一些可能不被某些 DNS 客户端程序接受的标签。" (https://www.rfc-editor.org/rfc/rfc2181#section-11)

注意事项 #2:

"还有一个额外的规则,基本上要求顶级域名不是全部由数字组成的" (https://www.rfc-editor.org/rfc/rfc3696#section-2)

考虑到这两个方面,正确的正则表达式如下:

/^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$/i

请参见演示 @ http://regexr.com/3g5j0


这个答案比其他任何答案都更接近现实。它应该被接受为正确答案。 - nirvana-msu

0
以下表达式
(^((?=^.{4,253}$)(((http){0,1}|(http){0,1}|(ftp){0,1}|(ws){0,1})(s{0,1}):\/\/){0,1})((((?!-)[\pL0-9\-]{1,63})(?<!-)(\.)){1,})(((?!-)[a-z0-9\-]{1,63})(?<!-)((\/{0,1}[\pL\pN?=\-]*)+){1})$)

将会匹配

https://www.tes1t.com/lets/to?878932572
https://www.test.co.uk/lets/to?878932572
http://www.test.com/lets/to?878932572
http://www.test.co.uk/lets/to?878932572
ftp://www.test.com/lets/to?878932572
subdomain.test.com/lets/to?878932572
subdomain.test.com/lets/to?878932572
subdomain.subdomain.test.net/lets/to?878932572

sub-domain.test.net/lets/to?878932572
sub-domain.test.net/lets-go/to?878932572
www.test.net/lets/to?878932572
www.test-test.com/
www.test-test.com

subdomain.subdomainsubdomainsuèdomainsubdomainsubdomainsubdomainsubdomain.net/let2s/to?=878932572

www.test-test.co.uk
http://www.test-test-.com/test
www.test-teèst.co.uk/lets
www.test-test.co.uk/lets/
www.test-test.co.uk/lets/to?
test-test.co.uk/lets/to?
test-test.co.uk/lets/
test-test.co.uk/lets
test-test.co.uk
http://test.com/lets/to?878932572
https://test.com/lets/to?878932572
ftp://test.com/lets/to?878932572
ftps://test.com/lets/to?878932572
ws://test.com/lets/to?878932572aa
wss://test.com/lets/to?=878932572bar
test.com

subdomain.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.test.khbdomainsubdomainsubdomain.test.net/lets/to?87893257

但不匹配:

www.-test-fail-.com
www.-test-fail.com
-test-fail.com
test-fail-.com

subdomain.subdomainsubdomainsubdomainsubdomainsubdomainsubdomainsubdomainsubdomainsubdomainsubdomainubdomainsubdomainsubdomain.test.net/lets/to?878932572

subdomain.subdomainsubdomainsubdcnvcnvcnofhfhghgfhvnhj-mainsubdomainsubdohhghghghfhgffgjh-gfhfdhfdghmainsubdocgvhngvnbnbmghghghaihgfjgfnfhfdghgsufghgghghhdfjgffsgfbdomainsubdomainsubdomainsubdomainsubdomainsubdomainsubdomain.test.net/lets/to?878932572

subdomain.test.test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test..test.khbdomainsubdomainsubdomain.test.net/lets/to?87893257

1
你正在匹配 URL,而不是域名。 - thisismydesign

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接