Skip to main content
2 of 4
deleted 578 characters in body
Your Common Sense
  • 9.1k
  • 1
  • 22
  • 51

Email Validation in PHP

Is this script sufficient enough to validate user email input?

<?php 
//1 DATABASE CONNECTION
$dbHost = "HOST";
$dbUser = "USER";
$dbPassword = "PASSWORD";
$dbName = "DATABASE";

try {
  $dsn = "mysql:host=" . $dbHost . ";dbname=" . $dbName;
  $pdo = new PDO($dsn, $dbUser, $dbPassword);
  $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
  echo "DB Connection Failed: " . $e->getMessage();
  exit(0);
}
//1 END

//2 ADD EMAIL TO DATABASE

//set date and time
date_default_timezone_set('America/Los_Angeles');
$timestamp = strtotime('NOW');
$dateTime = date('Ymd-His', $timestamp);

//variable to store ipv4 address
$userIP4 = gethostbyname($_SERVER['REMOTE_ADDR']);
//storing ip6 could be something like: "bin2hex(inet_pton($_SERVER['REMOTE_ADDR']));" but I couldn't figure out if the output was correct, because it looked nothing like an ipv6 address.....

if(filter_var($userIP4, FILTER_VALIDATE_IP)) {
    //yes it's valid IPv4
    if($_SERVER['REQUEST_METHOD'] == 'POST') {
        $email = htmlspecialchars($_POST['email']); //convert special characters to HTML entities (&,",<,>)
        $Temail = trim($email); //trim spaces on ends
        
        //allow international characters
        if(preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$^/", $Temail)) {
            //prevents invalid email addresses
            header("Location: invalid.html");
            exit (0);
        } else {
            //Check Email Domain MX Record
            $email_host = strtolower(substr(strrchr($Temail, "@"), 1));
            if (!checkdnsrr($email_host, "MX")) {
                header("Location: invalid.html");
                exit (0);
            } else {
                //Prevent users from inputting a specific domain...
                $notallowed = [
                    'mydomain.com',
                ];
                $parts = explode('@', $Temail); //Separate string by @ characters (there should be only one)
                $domain = array_pop($parts); //Remove and return the last part, which should be the domain
                if ( ! in_array($domain, $notallowed)) {

                    //checks database to make sure the email is not a duplicate
                    $stmt1 = $pdo->prepare("SELECT * FROM emailTable WHERE email=?");
                    $stmt1->execute([$Temail]);
                    $user = $stmt1->fetch();
                    if($user) {
                        //prevents adding a duplicate email
                        header("Location: duplicate.html");
                        exit (0);
                    } else {
                        //generate Activation code
                        $Acode = md5(time().$Temail);
                        
                        //send verification email
                        $emailfrom = '[email protected]';
                        $fromname = 'MY NAME';
                        $subject = 'Confirm Your Email Subscription';
                        $emailbody = "
                            <html>
                            <body style='background-color: #000; padding: 15px;'>
                                <table style='background-color: #222;'>
                                    <tr style='background-color: #333; padding: 15px; font-size: 1.3rem;'>
                                        <td><h2 style='color: #FFF;' align='center'>Please Verify Subscription</h2></td>
                                    </tr>
                                    <tr>
                                        <td style='color: #FFF; font-size: 1.1rem;' align='center'>
                                            <br/>
                                            <br/>
                                            If you didn't sign up for my email list, simply delete this message. You will not be added unless you push the button below.
                                            <br/>
                                            <br/>
                                        </td>
                                    </tr>
                                    <tr>
                                        <td style='color: #FFF; font-size: 1.3rem;' align='center'>
                                            <button style='background-color: #000; width: 6rem; height: 2rem;'><a href='https://www.MYDOMAIN.com/verify.php?acode=$Acode' style='color: #F00; text-decoration: none; font-size:1rem;'>VERIFY</a></button>
                                            <br/>
                                            <br/>
                                        </td>
                                    </tr>
                                    <tr>
                                        <td style='color: #FFF; font-size: 1.1rem;' align='center'>
                                            <font style='font-size:0.8rem;'>This email was automatically generated from a mailbox that is not monitored.</font>
                                        </td>
                                    </tr>
                                </table>
                            </body>
                            </html>";
                            
                        $headers = "Reply-To: MY NAME <[email protected]>\r\n"; 
                        $headers .= "Return-Path: MY NAME <[email protected]>\r\n"; 
                        $headers .= "From: MY NAME <[email protected]>\r\n";  
                        $headers .= "MIME-Version: 1.0\r\n";
                        $headers .= "Content-type: text/html; charset=UTF-8\r\n";
                        $headers .= "X-Priority: 3\r\n";
                        $headers .= "X-Mailer: PHP". phpversion() ."\r\n" ;
    
                        $params = '-f ' . $emailfrom;
                        $send = mail($Temail, $subject, $emailbody, $headers, $params); // $send should be TRUE if the mail function is called correctly
                        if($send) {
                            //add the new email and other data to the database
                            $sql = "INSERT INTO emailTable (IP4, datetime, email, acode) VALUES (:IP4, :datetime, :email, :acode)";
                            $stmt2 = $pdo->prepare($sql);
                            $stmt2->execute(['IP4' => $userIP4, 'datetime' => $dateTime, 'email' => $Temail, 'acode' => $Acode]);
                            $userIP4 = "";
                            $dateTime = "";
                            $Temail = "";
                            $Acode = "";
                            header("Location: success.html");
                            exit (0);
                        } else {
                            header("Location: invalid.html");
                            exit (0);
                        }
                    }
                } else {
                    header("Location: notallowed.html");
                    exit (0);
                }
            }
        }
    } else {
        header("Location: invalid.html");
        exit (0);
    }
} else {
    header("Location: invalid.html");
    exit (0);
}
//2 END
?>

Security threats in mind:

1. SQL Injections!!! --- Solutions: Prepared Statements (PDO), using only UTF-8, and including "$bpdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);" in the database connection

2. XSS Attacks!!! --- Solutions: htmlspecialchars(), Content-Security Policy (placed in htaccess):

<FilesMatch "\.(html|php)$">
    Header set Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: 'unsafe-inline'; media-src 'self' data: 'unsafe-inline'; connect-src 'self';"
</FilesMatch>

3. OS Command Attacks!!! --- Solutions: Striping whitespace (not necessary with emails), validating against a whitelist of permitted values.

4. DOS Attacks!!! --- Solution: None implemented. I'm unsure if any additional precaution is necessary, since there are no login possibilities on my website.

5. PHP Email Injection!!! --- Solution: A Regular Expression (the one I have is mostly designed to allow for international characters).

Additionally, I use an SSL Certificate, SiteLock Security- Essential, CloudFlare CDN, and have implemented a DMARC Policy in my DNS (something I'll be fine tuning for the foreseeable future).

user231248