使用正则表达式验证电子邮件地址是否会造成伤害?

17

我听说用正则表达式验证电子邮件地址是一件不好的事情,实际上会引起危害。为什么会这样呢?

我曾认为验证数据永远不可能是一件坏事,也许是没有必要的,但只要你正确地执行验证,就绝不会有问题。这个做法到底对还是错呢?如果会造成危害,请给出一个例子。


4
仅在购买时“验证”信用卡号为什么是不好的事情? - user2864740
2
通常验证电子邮件地址是不错的。 - Xatenev
2
这个问题正在meta上讨论 - cigien
8个回答

39

通常情况下,使用正则表达式验证电子邮件地址是有害的。这是由于正则表达式作者做出了错误的假设。

正如klutt所指出的,电子邮件地址包含两个部分:local-partdomain。值得注意的是,关于这些部分的一些事情并不立即显而易见:

  • local-part可以包含转义字符甚至额外的@字符。
  • local-part可能区分大小写,但具体是由该特定域名的邮件服务器决定如何区分大小写。
  • domain部分可以包含零个或多个标签,由句点(.)分隔,尽管实际上没有对应于根(零标签)或TLD(一个标签)本身的MX记录

因此,在不拒绝与上述相对应的有效电子邮件地址的情况下,您可以进行一些检查:

  • 地址至少包含一个@
  • local-part(右侧最后一个@之前的所有内容)不为空
  • domain部分(右侧最后一个@之后的所有内容)包含至少一个句点(再次,这并不严格正确,但很实用)

就是这样。正如其他人所指出的,最佳实践是测试该地址的可交付性。这将建立两个重要的事情:

  1. 电子邮件当前是否存在;以及
  2. 用户是否可以访问该电子邮件地址(是否为合法的用户或所有者)

如果将电子邮件激活过程纳入业务流程中,则无需担心具有问题的复杂正则表达式。

进一步阅读:

RFC 5321:简单邮件传输协议

OWASP:输入验证防范清单


3
关于可能造成的危害 - 如果您有一个错误的负面结果(拒绝一个有效的电子邮件地址),那么您就会拒绝那些本来可能会为您的商品付款的合法用户。 - bly
1
那不是什么伤害;虽然有点不幸,但该用户可以创建一个新的电子邮件账户,并且很可能可以轻松地将其自动转发。 - MrMesees
2
从经济的角度来看,阻止/驱赶潜在用户使用您的系统必定是有害的,我不认为这会有任何好处。我在大多数网站上使用电子邮件字符串的密码,例如 rogue+somesecretthing@abc.xyz,我决定不在不允许 + 的网站上注册的次数已经不止一次了。 - Rogue
1
@Rogue 这个论点也可以用于接受替代的日期和时间格式以及任何会增加复杂性的事情。也许用户不喜欢以你喜欢的格式上传图片。你需要在某个地方设限。用户可以相当好地控制他们的电子邮件。在这里没有被禁止或讨论的 + 号,我使用它,所以如果有人阻止它,那就糟糕了,但这是不常见的,并且对他们来说是权衡,在能够精确匹配和聚合跨服务的电子邮件之间进行选择。我认为让他们做出自己的选择。我们会做出我们自己的选择。 - MrMesees
2
@MrMeses 我同意。这是一个平衡的问题。一方面,用户可以拥有一个转义的双引号电子邮件地址,但这种情况非常罕见,你可能永远不会遇到它,而当你遇到它时,说实话,那个用户是故意使用奇特的方式,你的网站并不是他们第一次遇到问题。另一方面,像移动设备上的自动更正在点后添加空格这样的错别字经常发生,并且也会对点击或转化造成障碍。不考虑后者而支持前者似乎不明智。您可以允许+而不放弃正则表达式。 - Kyle Alm
显示剩余4条评论

19

简短版

除非你有充分的理由使用正则表达式验证电子邮件,否则不要使用它们。最好使用验证邮件来进行验证。在大多数情况下,一个简单检查字符串是否包含@符号的正则表达式就足够了。

简洁版本

在大多数情况下,问题“如何使用正则表达式验证电子邮件地址”很可能是一个XY问题,因为它很可能不是解决您实际问题的方法。真正的问题可能是“如何确保用户输入的电子邮件地址可以用于与用户进行通信?”或者正如zsalya在评论中提到的,“在将用户输入的电子邮件地址存储到数据库之前,应该对其进行哪些净化处理?”

构建用于验证电子邮件的正则表达式可以是一项有趣且有益的练习,但通常情况下,在生产代码中应该尽量避免使用它。验证电子邮件地址的正确方法在大多数情况下是发送验证邮件。试图验证邮件地址是否符合规范非常棘手,即使你做对了,这仍然经常是无用的信息,除非你知道它是一个可以发送邮件并且有人会阅读的邮件地址。
想一想吧。你有多频繁地需要存储一个错误的邮件地址?
如果你只是想确保用户不会混淆输入字段,请检查邮件地址是否包含一个“@”字符。那就足够了。好吧,这样做可能无法捕捉到那些坚持在用户名或密码中使用该字符的人,但那是他们的麻烦。;)
长篇版本
在大多数情况下,仅仅知道电子邮件地址是否有效并没有什么意义。你真正想知道的是它是否是正确的电子邮件地址。
原因可能各不相同。您可能想要发送通讯,用于定期沟通、密码恢复或其他用途。但无论是什么,重要的是它是正确的地址。知道地址是否符合复杂的标准并不重要,唯一重要的是知道它是否可以用于存储地址的目的。
验证的正确方法是通过发送带有验证链接的邮件。
如果您已经通过验证链接验证了电子邮件地址,通常就没有必要再检查它是否是正确的电子邮件地址,因为您知道它有效。然而,它可以用于基本上检查用户是否在正确的字段中输入电子邮件地址。在这种情况下,我的建议是非常宽容。我认为只需检查字段中是否有一个“@”符号就足够了。这是一个简单的检查,所有的电子邮件地址都包含一个“@”。如果您想使它更复杂一些,我建议只是警告用户地址可能有问题,但不禁止使用。一个非常简单的正则表达式,几乎不会出现错误(如果有的话),是:
.+@.+\..+

这意味着在“@”之前有一个非空字符串,后跟一个非空域名、一个点和一个非空顶级域。但实际上,我会坚持使用“@.+”,这意味着右侧部分是非空的,并且我不知道任何接受空服务器名称的DNS服务器。
正确检查电子邮件是否符合标准实际上非常棘手。
但更糟糕的问题是,精确验证电子邮件地址的正则表达式实际上是非常复杂的事情。如果你试图自己创建一个正则表达式,几乎肯定会出错。这里值得一提的是,标准RFC 5322允许在括号内进行注释。更糟糕的是,允许嵌套的注释。标准的正则表达式无法匹配嵌套模式。您需要扩展的正则表达式来解决这个问题。虽然扩展的正则表达式并不少见,但它确实说明了其复杂性。而且,即使你弄对了,当新的标准出现时,你会更新正则表达式吗?
邮件服务器可能支持非标准地址。

还有一件事,即使你完全正确,可能仍然不足够。电子邮件地址在@符号左侧具有本地部分,在右侧具有域名部分。本地部分的所有内容都应由服务器处理。当然,RFC 5322对有效的本地部分的要求非常详细,但是如果特定的电子邮件服务器接受不符合RFC 5322的地址,那该怎么办?您真的确定不想允许一个有效的电子邮件地址,只因为它不遵循标准吗?您是否愿意因为客户选择了一个不常见的电子邮件供应商而失去业务?或者因为您在正则表达式中犯了一个错误?(提示:使用特定语言字符时很容易出错)

我在这里可以补充一下,我曾经因为我的电子邮件地址无法注册到各种网站。而且我的地址并不奇怪。它只是简单的<name>@protonmail.com,但有些网站声称它不是有效的地址。我很难相信这是因为<name>,因为它只包含来自a-z的12个小写字母。

如果你真的想在生产代码中检查地址是否正确,那么可以使用MailAddress类或类似的东西。但首先花一分钟思考一下,这真的是你想要的吗?问问自己,如果地址不正确,它还有价值吗?如果答案是否定的,那就不要这样做。使用验证链接代替。
话虽如此,验证输入也是一件好事。重要的是要知道为什么要这样做。使用正则表达式或者(最好的情况下)像Mailaddress类这样的方法来验证电子邮件地址可以提供一些保护,防止恶意输入,比如SQL注入等。但如果这是你唯一的防范恶意输入的方法,那么你肯定在其他方面做错了什么。

3
我同意需要一个验证链接,但这并不意味着验证是毫无意义的。 - Tim Biegeleisen
我的观点是,如果你知道电子邮件地址有效,那么几乎没有必要检查它是否符合某些标准。你不同意吗? - klutt
2
@klutt 我不同意验证器可以捕获某些形式的用户输入错误(或者甚至是恶意输入,这取决于如何定义)。这就是为什么物理地址足够验证,尽管它们通常无法在邮件发送/接受之前“证明”。(物理地址比电子邮件地址更难验证,所以...再说一遍,这就是外部库和服务的作用。) - user2864740
3
@klutt 通常你会尝试为那些可能会意外输入错误电子邮件地址的用户(https://en.wikipedia.org/wiki/Luser)设计软件。他们可以成功注册,但无法收到电子邮件,可能永远不会再回到你的页面。当然,你无法捕捉到所有可能输入的形式,但尽可能地帮助他们是有意义的。 - Xatenev
1
如果您使用“type=email”字段并将REQUIRED设置为true来验证表单,然后您收到一个无效或缺少电子邮件字段的表单输入,那么您就知道这很可能是POST来自机器人而不是真实用户,并可以相应地采取行动。 - Marc Wilson
显示剩余3条评论

9
除了其他答案之外,我想指出,使用回溯的正则表达式引擎容易受到ReDoS - 正则表达式拒绝服务攻击的影响。攻击基于这样一个事实:许多非平凡的正则表达式的输入可能需要大量的CPU周期才能产生非匹配项。
制作这样的输入甚至可能会对可用性造成麻烦,即使只是使用小型僵尸网络。
缓解此问题的方法:
  • 通常可以重写正则表达式以避免灾难性的回溯;或:
  • 使用不支持回溯的正则表达式引擎 - 尽管大多数引擎都支持它,但确实存在不支持此类功能的引擎 - 一个著名的例子是Go / Golang使用的RE2正则表达式引擎。
更多信息请参见:" Regular Expressions Denial of the Service (ReDoS) Attacks"

这是误导性的:只有使用回溯的正则表达式引擎才会容易受到DoS攻击;即使如此,正则表达式也必须以这样的方式编写才能容易受到攻击。通常可以重写正则表达式以避免漏洞,即使在回溯引擎上也是如此。 - awwright
@awwright 我更新了答案,但我认为它并没有误导。原始答案中没有使用“所有正则表达式引擎”或“所有非平凡表达式”等不正确的概括。如果读者想要了解更多信息,那么一个有思考能力的读者可能会阅读文章而不是这篇简短的帖子(这就是为什么我链接了文章 - 为了避免在答案中写下所有细节和细微差别)。 - Mindaugas Bernatavičius

2

如果您的正则表达式格式不正确,则可能会拒绝有效的电子邮件地址。这适用于任何“电子邮件验证”规则。

我知道有一个常常被拒绝的电子邮件地址,它没有任何奇怪之处,只是很长而已。因为 @ 前面的部分是他们的法定名称,所以它真的让这个人很烦恼——在电子邮件地址方面显然是一个明显的选择。

这是错误地进行电子邮件验证的潜在危害的一部分:通过拒绝输入系统中的有效电子邮件地址来使用户感到困扰。


2
验证电子邮件地址本身并不是坏事。
甚至使用正则表达式验证电子邮件地址也不是坏事,尽管有更好的方法来验证它们。
真正的问题在于基于语法验证电子邮件地址是无效的:
它不能告诉你该地址是否对应于有效的工作邮箱。
它不能告诉你它是否为正确用户(或代理)的地址。
由于用户经常意外(或故意)输入语法上有效但不正确的电子邮件地址,如果您需要知道该地址是否为涉及人员的正确地址,则需要执行其他操作。例如,您可以向提供的地址发送某种“激活”或“确认”电子邮件。
因此,假设您要实施第二阶段的检查,电子邮件地址的语法检查第一阶段相对不重要,甚至不是必需的。
创建一个正确处理电子邮件语法中所有边缘情况的正则表达式是非常困难的。但是,禁止一些更深奥的边缘情况可能是可以接受的,前提是它不会给大量用户带来不便。
正则表达式验证几乎无法过滤出故意伪造的电子邮件地址。

1
我听说使用正则表达式验证电子邮件地址是一件坏事,实际上可能会造成伤害。为什么会这样?
这是正确的。正则表达式解决方案很有吸引力,因为电子邮件地址是一个结构化字符串,而正则表达式用于在字符串中查找结构。
但这也是错误的解决方案,因为当您要求用户提供电子邮件地址时,通常是为了与他们联系。
验证不正确的原因是:
- 地址可能是有效的,但不是用户可以访问的地址。我可以在任何表单中填写地址billgates@microsoft.com,并且它可能会被接受为有效的电子邮件地址(免责声明:我不是比尔·盖茨 :))。 - 电子邮件地址的语法非常棘手,很难正确地获取(请参见此处的示例),通过定义自己的电子邮件验证正则表达式,您将最终拒绝有效地址并接受无效地址。
我认为验证数据永远不可能是一件坏事。

验证数据并不是坏事。但是在这种情况下,您将为应用程序提供一个设计有缺陷的功能:

对于开发人员来说,您的应用程序似乎正在验证输入,但验证可能是不必要的、可能不完整的,并且在验证结束时,您不知道是否有一个地址可以让您联系用户。

也许是不必要的,但只要正确执行验证,就永远不会是坏事。

它不是不必要的;它是必要的。只是正则表达式是错误的工具。

归根结底,检查地址是否对用户有效的最佳方法是使用唯一令牌交换该地址:

  • 向该地址发送一封电子邮件,包含一个唯一的随机令牌(将令牌与用户数据存储)
  • 在电子邮件中要求用户“点击链接/按钮”,实际上将令牌发送回给您。
  • 验证令牌。

-2

正则表达式可能是验证电子邮件地址的最佳方法,只要您使用正确的表达式。一旦您使用正则表达式检查了地址,只需要检查几个附加要求(地址不太长且有效的UTF-8编码)。

这是因为定义电子邮件地址格式的ABNF语法是“正则”的,这意味着它可以被描述为一个正则表达式;没有回溯、递归或任何非正则特征。

这只是理解规范的问题;但一旦您理解了规范,就会发现电子邮件地址的正则表达式实际上非常简单:如何使用正则表达式验证电子邮件地址?


-2

正则表达式并不会造成伤害。

使用一个好的电子邮件正则表达式来过滤那些急躁的虚假用户。

如果你要向那个人销售产品,你可能需要联系他们进行进一步验证,尽管卖家并不太关心电子邮件,只要验证信用卡就足够了。

否则,唯一需要验证的地方就是当有人想要访问和与你的论坛互动时,而你希望通过将他们的电子邮件出售给大规模广告商来获得报酬,即使你说你不会这样做。

HTML5 规范中的通用电子邮件正则表达式如下 -

^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$

http://www.w3.org/TR/html5/forms.html#valid-e-mail-address

 ^
 [a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+
 @
 [a-zA-Z0-9]
 (?:
      [a-zA-Z0-9-]{0,61}
      [a-zA-Z0-9]
 )?
 (?:
      \.
      [a-zA-Z0-9]
      (?:
           [a-zA-Z0-9-]{0,61}
           [a-zA-Z0-9]
      )?
 )*
 $

“过滤那些不耐烦的虚假用户”有何价值?如果这样的用户无法通过正则表达式检查,他们只会用类似于dfsjalkdsfahj@example.com之类的内容替换它,并结束讨论。在我看来,这没有任何价值,其他得票更高的答案详细阐述了这一点。 - Nicofisi

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