我应该在将电子邮件地址传递给is_email()函数之前对其进行清理吗?


13

我正在is_email()检查用户提供的电子邮件地址是否有效。例如:

$email = $_POST['email'];
if ( is_email( $email ) )
    // Do something.

据我所知,此函数中没有任何内容可将信息写入数据库。$email在将其传递给功能之前是否应该进行消毒?


Kaiser,谢谢您的编辑。这实际上对我来说很卫生,但是我敢肯定,这里的大多数读者都会使用z :)
henrywright

Answers:


5

看一下is_email()trac 上的功能,您似乎不需要进行简单的测试,因为它只是字符串测试。我什至可以说,如果该函数返回true,则无需在将其发送到数据库之前对其进行清理。


我的想法完全关于字符串测试。我想我会在发送到数据库之前仍然要进行清理,您可能是正确的,这是没有必要的,但是当涉及到这些事情时,我感到非常不安:)
henrywright 2014年

没错,安全性要比后悔好,而且消毒的开销将是完全不明显的。
Howdy_McGee

18

WordPress和PHP核心

is_email()函数是一个典型的WordPress的实现,不具有什么样的工作完全RFC 6531允许。原因之一可能是,根据Internet工程任务组(IETF®)指南,默认的PHP FILTER_VALIDATE_EMAIL常量filter_var()在验证某些内容方面并不好得多。

标准品

关键是RFC 6531允许“超出ASCII范围的Unicode字符”。即是(对于本地部分-之前的@):

  • 大写和小写英文字母(az,AZ)(ASCII:65–90、97–122)
  • 转换09(ASCII:48–57)
  • 这些特殊字符: ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  • 字符.(点,句点,句号)(ASCII:46),前提是它不是第一个或最后一个字符,并且还不能连续出现(例如John..Doe@example.com,不允许)。
  • 特殊字符是有限制的。他们是:
    • 空格和"(),:;<>@[\](ASCII:32、34、40、41、44、58、59、60、62、64、91–93)
    • 特殊字符的限制是必须仅在引号之间使用特殊字符,并且其中两个字符(反斜杠\和引号“(ASCII:92,34))也必须带有反斜杠\(例如"\\""\"") 。
  • 注释可以在局部的任何一端加上括号。例如john.smith(comment)@example.com(comment)john.smith@example.com都等同于"john.smith@example.com",但john.(comment)smith@example.com将无效。
  • U+007FRFC 6531允许使用编码为UTF-8的上述国际字符,尽管邮件系统可能会在分配本地部分时限制使用哪些字符。

对于全局/域部分:

电子邮件地址的域名部分必须符合严格的准则:它必须符合主机名的要求,该主机名由字母,数字,连字符和点组成。另外,域部分可以是IP地址文字,并用方括号括起来,例如jsmith@[192.168.2.1]jsmith@[IPv6:2001:db8::1][…]

资料来源:维基百科

什么有效?

这可能会导致奇怪但有效的电子邮件地址,如下所示:

  • localpart.ending.with.dot.@example.com
  • (comment)localpart@example.com
  • "this is v@lid!"@example.com
  • "much.more unusual"@example.com
  • postbox@com
  • admin@mailserver1
  • "()<>[]:,;\\@\"\\\\!#$%&\'*+-/=?^_`{}| ~.a"@example.org
  • " "@example.org

资料来源: php.net/作者gt@kani.hu –本文作者固定的示例

限度

还有本地和域长度限制:

电子邮件地址的格式是local-part@domain本地部分可以是最多64个字符长和域名可以具有最大的253个字符 -但最大256个字符长度的正向或反向路径的限制整个电子邮件地址是没有长度超过254个字符。[2]的正式定义在RFC 5322(第3.2.3和3.4.1)和RFC 5321 -与在信息RFC 3696 [3]和相关联的勘误表给出一个更可读的形式。

资料来源:维基百科

WordPress限制

这就是WordPress检查的内容:

  • 测试电子邮件的最小长度可以是: strlen( $email ) < 3
  • 测试第一个位置之后的@字符: strpos( $email, '@', 1 ) === false
  • 测试无效字符: !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local )
  • 测试期间的顺序: preg_match( '/\.{2,}/', $domain )
  • 测试前期和尾期以及空格: trim( $domain, " \t\n\r\0\x0B." ) !== $domain
  • 假设该域名将至少有两个替补:$subs = explode( '.', $domain );然后
    • 2 > count( $subs )
    • trim( $sub, " \t\n\r\0\x0B-" ) !== $sub
    • !preg_match('/^[a-z0-9-]+$/i', $sub )

资料来源:WP Core v4.0

过滤器和自定义验证

上述所有情况将触发is_email()返回false。结果是可过滤的(可以附加回调),过滤器将具有三个参数,最后一个参数是原因。例:

return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' );

这意味着您可以覆盖特定检查返回的结果。

这允许您添加特殊检查,例如允许Umlaut域,仅TLD的域部分等。

结论

WordPress在大多数情况下都是安全的,但由于邮件服务器实际上必须符合RFC的要求,因此更具限制性。请记住,并非每个邮件服务器都符合RF 6531准则。

编辑

有趣的副作用:内部有两个相关功能~/wp-includes/formattingis_email()sanitize_email()。它们实际上是相同的功能。我不知道为什么有人认为将函数内容从一个复制到另一个是一个好主意,而不仅仅是将一个作为回调添加到另一个提供的过滤器中。由于v0.71v1.5相同,因此我个人会在以后使用清理后的字符串时使用后者。请注意,甚至声明它不符合RFC。is_email() sanitize_email() is_email()


所以您是说,从理论上讲,会有一些根据RFC 6531完全有效的电子邮件地址,但是WordPress会认为这些地址无效吗?
henrywright

是的。例如,仅TLD域名,变音符号域名等,您可以在答案结论之前的最后一段中阅读。请再次阅读答案。我知道缠住你的头很多,但这是值得的。
kaiser 2014年

1
我实际上已经读过两次,因为这值得一读!感谢您提供如此详细的答案:)
henrywright 2014年

2

消毒所有东西!

安全的基本规则之一是永远不要信任用户的输入。通常,我不在乎is_email()或任何其他特定函数的实现,或者该函数是否会对我提供的内容造成任何危险。也许实施会有一天会改变。谁知道。我必须假设它可以被破坏。始终应该假设用户输入是积极的敌意,对于最终发往数据库的任何东西都是双重的,并且在将用户输入移交给某些功能之前先对其进行净化。这只是良好的常规安全卫生措施。


我认为您说自己永远不知道实施方式是否会发生变化时,您的想法就打了。现在不进行消毒可能还可以,但是谁知道以后是否会改变呢?
henrywright 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.