24

I have a class that validates every input before I send it to the database layer. Note that my problem is not about escaping or anything. My database layer will handle the SQL Injection problem. All I want to do is validate if the email is valid or not because later that email might be used as a 'send to'. For instance, the user will recover access to his account through a link sent to the e-mail. I read a lot about filter_var and there are a bunch of people being against it and other bunch being in favor. Keeping the focus on 'I just want to validate email and not filter it for database or for html or XSS or whatever', is there a problem in using filter_var?

Marco Aurélio Deleu
  • 4,279
  • 4
  • 35
  • 63
  • 5
    If you want to make sure the email is formatted correctly, it's fine for that purpose. If you want to know if it is a real email address, then you'll have to pay for an email validation service most likely. – Jeremy Harris Oct 22 '13 at 15:30
  • 4
    You don't need to pay for a validation service. Just send an email with a verification link. – ComFreek Oct 22 '13 at 15:32
  • YES, you should definitely use `filter_var` to check if this is a valid email address, as this is the only reliable way in PHP to check an email address… – feeela Oct 22 '13 at 15:35

6 Answers6

26

Yes, you should.

Using the standard library validation instead of a home brew one has multiple benefits:

  1. Many eyeballs already seen the code (well, at least two) you will be using, hopefully with experience in email validation even before it's merged into a release.
  2. It's unit tested.
  3. Other people will use the same check and report bugs and you get these fixes for free on php updates.

However checking the format of an email address is only the first line of defense, if you really want to know that it's real or not, you will have to send a message to it.

complex857
  • 20,425
  • 6
  • 51
  • 54
  • 9
    Unfortunately, this is only true if somebody actually maintains & further improves such a validation library. In this case using `filter_var` has some dangers: it does not support UTF8 in the local part even though this has been valid for quite some time, and you actually have to convert the domain name with `idn_to_ascii` yourself to validate international domain names, which is not obvious. Prepare yourself for many failed email addresses which are actually valid if you use `filter_var`, and this will only get much worse when unicode is more widely used in email addresses. – iquito May 19 '16 at 17:54
  • **This answer is no longer true in 2023.** See [my answer](https://stackoverflow.com/a/67692441/1049833) for details. – tyteen4a03 Feb 01 '23 at 17:29
11

Yes, you should use filter_var and this is how you can incorporate it :

if( filter_var( $email ,FILTER_VALIDATE_EMAIL ) )
{
    /*
     * Rest of your code
     */
}
Uours
  • 2,517
  • 1
  • 16
  • 21
absfrm
  • 352
  • 2
  • 14
8

Yes. But checkdnsrr() may also be worth a mention here.

filter_var() will approve domains that seem to be incomplete because it could be valid in a local context (e.g. someone@localhost). This can lead to false positives where people just miss off the TLD or the dot in the domain name (e.g. hattie.jacques@gmailcom)

Catch these by doing a checkdnsrr() lookup on the domain - if you can find an MX record for the domain and the address is valid then you've pretty much done your best.

Example code:

if(filter_var($email, FILTER_VALIDATE_EMAIL)) 
{
    list($userName, $mailDomain) = explode("@", $email);
    if (!checkdnsrr($mailDomain, "MX"))
    {
        // Email is unreachable.
    }
}
else
{
    // Email is bad.
}

checkdnsrr() is pretty instant (in my experience) and I've not yet found an environment in which it doesn't work.

AJReading
  • 1,193
  • 20
  • 35
richplane
  • 537
  • 4
  • 12
  • I'll give it a check when I have some time, but off-hand I can assure you that the example that you mentioned @gmailcom does NOT gets validated by filter_var. – Marco Aurélio Deleu Dec 11 '14 at 18:37
  • True enough. Which is a problem in itself, albeit only in pretty obscure circumstances. There's a [PHP bug report](https://bugs.php.net/bug.php?id=49576) listing failings of FILTER_VALIDATE_EMAIL; doing checkdnsrr() would catch all the false positives given in the codepad example there. – richplane Nov 03 '15 at 11:00
  • Note that checkdnsrr has a timeout of 20 seconds, which apparently cannot be changed (some domains respond in a way which makes it reach the 20 seconds, as I had to experience in my application), and the domain name should be processed by `idn_to_ascii()` first to support domains with special characters (like ö, é etc.). I switched to calling `dig` with `passthru` so I can specify a timeout and what nameservers to use. – iquito May 16 '16 at 23:23
  • 1
    `filter_var` fails on an email address like someone@localhost - even though this would be a valid email address in some contexts. – iquito May 19 '16 at 17:48
3

Unfortunately, filter_var does not support UTF8 in the local part (before the @) of the address, and to support international domain names you will need to run the domain name through idn_to_ascii separately (which is a hassle and not obvious).

This makes filter_var fairly useless in my opinion: the more unicode email addresses appear in the wild, the more legitimate email addresses will fail, which is especially the case for countries like China or Brazil, where there is an obvious demand for these addresses. filter_var also does not allow email addresses like root@localhost, which are valid and may be useful in a server context.

It would be really useful if an email library existed to validate according to specific instructions - are only domain names allowed, or also arbitrary hosts like localhost, or is there a whitelist for custom domains which should be valid? Should unicode be allowed? What are common typos for freemail domain names (like @homail.com) that should also fail?

Also, validating some domain names in more specific manner would be sensible - hotmail.com does not allow unicode characters for now and has specific limitations on useable characters. As most used email addresses in PHP applications are concentrated on maybe 100 different domains, this could be used to validate these domain names in a better way. Unfortunately, no such library exists yet, as far as I know.

iquito
  • 2,388
  • 1
  • 18
  • 26
  • You need to SANITIZE the email address, so your point really isn't that fully explaining things.. look into the filter options etc.. – Mike Q Jul 18 '17 at 15:11
3

In 2021, No. filter_var's validation implementation (as of PHP 8) does not verify international domain names, which I consider is big enough of a reason to ditch.

Symfony's own Email Validation also uses a Regex that has no UTF-8 support.

Use https://github.com/egulias/EmailValidator. It follows the email RFC and can also check MX records to further verify that the domain does exist.

tyteen4a03
  • 1,812
  • 24
  • 45
1

Some of the comments I saw didn't match my testing so I wanted to point out the functionality I found with PHP 5.x. If you first SANITIZE the email it will remove all of the chars you don't want, then you can VALIDATE. I have two functions in case someone wanted to just do one or the other.

Check if email is valid:

function isValidEmailAddress($email = '', $check_domain = false)
{
    if (empty($email)) {
        return false;
    } else {
        $success = true;
    }

    if (!filter_var((string) $email, FILTER_VALIDATE_EMAIL)) {
        $success = false;
    }
    if ($check_domain && $success) {
        list($name, $domain) = explode('@', trim($email) . "@");
        if (!checkdnsrr($domain, 'MX')) {
            $success = false;
        }
    }
    return array("success" => $success, "email" => $email);
}

Remove <> and UTF8 etc :

function sanitizeEmailAddress($email = '') {
    if (!empty($email)) {
        $email = filter_var(strtolower(trim($email)), FILTER_SANITIZE_EMAIL);
    }
    return $email;
}

Sample Usage:

// test -- 
$list = array('goodÂ@bad.com', 'mikeq@google.com', 'puser@porsche.us', 'quick@us', '', '<yo@marist.edu>', 'hello@us.edu', 'rgil@yahoo.com', 'abc@2r.edu', 'one2@e3.edu', 'key@gen.us');

foreach($list as $email) {

    $ret = isValidEmailAddress( sanitizeEmailAddress($email), false );
    if ($ret['success']) {
        echo "GOOD " . $ret['email'];
    } else {
       echo "BAD  " .$email;
    }
        echo "\n";
    }

Results :

GOOD good@bad.com
GOOD mikeq@google.com
GOOD puser@porsche.us
BAD  quick@us
BAD  
GOOD yo@marist.edu
GOOD hello@us.edu
GOOD rgil@yahoo.com
GOOD abc@2r.edu
GOOD one2@e3.edu
GOOD key@gen.us

if you use the domain option: $ret = isValidEmailAddress( sanitizeEmailAddress($email), true );

GOOD good@bad.com
GOOD mikeq@google.com
GOOD puser@porsche.us
BAD  quick@us
BAD  
GOOD yo@marist.edu
GOOD hello@us.edu
GOOD rgil@yahoo.com
BAD  abc@2r.edu
BAD  one2@e3.edu
BAD  key@gen.us
Mike Q
  • 6,716
  • 5
  • 55
  • 62
  • Very important to note that you should FILTER_SANITIZE_EMAIL first. This allows php to clean it up before it runs it through its only validation process. – Chris Dec 28 '18 at 19:11