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).
htmlspecialchars
is nonsense. Use this function to sanitize data to be written to a HTML document. You're not doing that anywhere... \$\endgroup\$