13

I'm using is_email() to check if a user-provided email address is valid. For example:

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

To the best of my knowledge, nothing in this function writes info to the database. Should I be sanitizing $email before I pass it to the function?

  • Kaiser, thanks for the edit. It's actually sanitisation for me but I'm sure most readers here will use a z :) – henrywright Nov 24 '14 at 17:28
5

Looking at the is_email() functionality on trac, it looks like you don't need to sanatizie as it's just string testing. I would even go so far as to say that if this function returns true, you wouldn't need to sanitize it before sending it into the database.

  • My thoughts exactly about string testing. I think I'll still sanitise before sending to the db, you're probably right that it's not necessary, but I'm a nervous wreck when it comes to these things :) – henrywright Nov 24 '14 at 15:16
  • True, better safe than sorry and the sanitization overhead would be entirely unnoticeable. – Howdy_McGee Nov 24 '14 at 15:25
18

WordPress and PHP core

The is_email() function Source is a typical WordPress implementation and does not work completely with what the RFC 6531 allows. One reason might be, that the default PHP FILTER_VALIDATE_EMAIL constant for filter_var() isn't much better at validating something according to the The Internet Engineering Task Force (IETF®) guidelines.

Standards

Point is that the RFC 6531 allows "Unicode characters beyond the ASCII range". Namely those are (for the local part - before the @):

  • Uppercase and lowercase English letters (a–z, A–Z) (ASCII: 65–90, 97–122)
  • Digits 0 to 9 (ASCII: 48–57)
  • These special characters: ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  • Character . (dot, period, full stop) (ASCII: 46) provided that it is not the first or last character, and provided also that it does not appear consecutively (e.g. John..Doe@example.com is not allowed).
  • Special characters are allowed with restrictions. They are:
    • Space and "(),:;<>@[\] (ASCII: 32, 34, 40, 41, 44, 58, 59, 60, 62, 64, 91–93)
    • The restrictions for special characters are that they must only be used when contained between quotation marks, and that 2 of them (the backslash \ and quotation mark " (ASCII: 92, 34)) must also be preceded by a backslash \ (e.g. "\\" and "\"").
  • Comments are allowed with parentheses at either end of the local part; e.g. john.smith(comment)@example.com and (comment)john.smith@example.com are both equivalent to "john.smith@example.com", but john.(comment)smith@example.com would be invalid.
  • International characters above U+007F, encoded as UTF-8, are permitted by RFC 6531, though mail systems may restrict which characters to use when assigning local parts.

and for the global/domain part:

The domain name part of an email address has to conform to strict guidelines: it must match the requirements for a hostname, consisting of letters, digits, hyphens and dots. In addition, the domain part may be an IP address literal, surrounded by square braces, such as jsmith@[192.168.2.1] or jsmith@[IPv6:2001:db8::1] […]

Source: Wikipedia

What is valid?

This can lead to strange, but valid e-mail addresses like the following:

  • 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

Source: php.net / author gt@kani.hu – example fixed by author of this post

Limits

There are also local & domain length limits:

The format of email addresses is local-part@domain where the local-part may be up to 64 characters long and the domain name may have a maximum of 253 characters – but the maximum of 256-character length of a forward or reverse path restricts the entire email address to be no more than 254 characters long.[2] The formal definitions are in RFC 5322 (sections 3.2.3 and 3.4.1) and RFC 5321 – with a more readable form given in the informational RFC 3696[3] and the associated errata.

Source: Wikipedia

WordPress restrictions

And this is what WordPress checks for:

  • Test for the minimum length the email can be: strlen( $email ) < 3
  • Test for an @ character after the first position: strpos( $email, '@', 1 ) === false
  • Test for invalid characters: !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local )
  • Test for sequences of periods: preg_match( '/\.{2,}/', $domain )
  • Test for leading and trailing periods and whitespace: trim( $domain, " \t\n\r\0\x0B." ) !== $domain
  • Assume the domain will have at least two subs: $subs = explode( '.', $domain ); and then
    • 2 > count( $subs )
    • trim( $sub, " \t\n\r\0\x0B-" ) !== $sub
    • !preg_match('/^[a-z0-9-]+$/i', $sub )

Source: WP Core v4.0

Filters & custom validation

All above mentioned cases will trigger is_email() to return false. The result is filter-able (a callback can be attached) and the filter will have three arguments, where the last argument is the reason. Example:

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

which means that you can override results returned by specific checks.

This allows you to add special checks, for example to allow Umlaut-domains, TLD-only domain parts, etc.

Conclusion

WordPress is safe for most cases, but more restrictive as mail servers actually have to be to be RFC compliant. Keep in mind that not every mail server will align with the RF 6531 guidelines.

Edit

Funny sidefact: There are two related functions inside ~/wp-includes/formatting: is_email() and sanitize_email(). They are practically the same function. I have no idea why someone decided that it would be a good idea to copy the function contents from one over to another instead of just adding the one as callback to the filters the other provides. As is_email() since v0.71 and sanitize_email() since v1.5 are the same, I personally would use the later as you get a cleaned string. Note that is_email() even states that it isn't RFC compliant.

  • So you're saying, in theory, there will be email addresses out there which are completely valid according to RFC 6531 but these will be deemed invalid by WordPress? – henrywright Nov 24 '14 at 16:26
  • Some yes. For example TLD-only domains, umlaut domains, etc. as you can read in the last paragraph before the conclusion in the answer. Please read the answer again. I know it's lot to get wrap your head around, but it's worth it. – kaiser Nov 24 '14 at 16:48
  • 1
    I actually already read it twice as this is something that's worth understanding! Thanks for such a detailed answer :) – henrywright Nov 24 '14 at 16:57
2

Sanitize all the things!

One of the cardinal rules of security is to never trust the input from the user. In general, I don't care about the implementation of is_email() or any other specific function, or if that function does anything dangerous with what I give it. Maybe the implementation will change some day. Who knows. I have to assume it can be compromised. The assumption should always be that user input is actively hostile, doubly so for anything eventually destined for a database, and to sanitize every bit of user input before handing it off to some function. This is just good, general security hygiene.

  • I think you hit the nail on the head when you said you never know if the implementation will change. It may be OK to not sanitise now, but who knows if that will change at a later date? – henrywright Nov 24 '14 at 22:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.