here is a code of a simple contact form that I have created, I believe that the form is server-side protected against empty fields, header injections, CSRF, XSS, bots using 'Google reCAPTCHA v3' and so on.. The call is done through AJAX.. I just wanted to share this code and I am open to any suggestions that can improve the security and the speed of the site.. also I wonder if there will be any extra useful meta tags to be used.. you can check the live site that I am working on here
Thank you..
index.php
<?php session_start(); ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rainbow Parenting</title>
<link rel="apple-touch-icon" sizes="180x180" href="icon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="icon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icon/favicon-16x16.png">
<link rel="manifest" href="icon/site.webmanifest">
<link rel="mask-icon" href="icon/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="keywords" content="...">
<meta name="description" content="...">
<meta name="subject" content="...">
<meta name="robots" content="index, follow"/>
<meta name="author" content="...">
<link rel="canonical" href="https://www.rainbow-parenting.com">
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.rainbow-parenting.com">
<meta property="og:title" content="...">
<meta property="og:description" content="...">
<meta property="og:image" content="https://www.rainbow-parenting.com/img/logo-temp.png">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- Theme style -->
<link rel="stylesheet" href="css/style-temp.min.css">
<!-- Normalize -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" >
<!-- Animations -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Notable" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Mali" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row text-center">
<div class="col">
<img class="animated bounce shake" alt="Logo" src="img/logo-temp.png" width="150" height="auto">
<div><br>
<h1 class="animated bounce infinite">COMING SOON!!</h1>
<h2 class="animated flipInX">We will be launching our new website in:</h2>
<div class="simply-countdown simply-countdown-one animated rollIn"></div>
<h3 class="animated flash">in the meanwhile you can still contact us by clicking the button below</h3>
</div>
<div class="text-center animated jello">
<button class="button-temp button-temp1" id="btn-show-modal" data-toggle="modal" data-target="#modalContactForm">Contact Us</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalContactForm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button><br>
<form id="contactForm" name="sentMessage" novalidate="novalidate" method="post">
<div class="row">
<div class="control-group"><i class="fas fa-user big-icon"></i>
<fieldset>
<input class="modal-input" type="text" id="name" placeholder=" " tabindex="1" required="required" data-validation-required-message="• Please enter your name." minlength="5" maxlength="20" pattern="[A-Za-z ]+" data-validation-pattern-message="Only letters & whiteapces.">
<span class="inputup">Your Name:</span>
</fieldset><p class="help-block text-danger"></p>
</div>
</div>
<div class="row">
<div class="control-group"><i class="fas fa-envelope big-icon"></i>
<fieldset>
<input class="modal-input" type="email" id="email" placeholder=" " tabindex="2" required="required" data-validation-required-message="• Please enter your email address.">
<span class="inputup">Your Email:</span>
</fieldset><p class="help-block text-danger"></p>
</div>
</div>
<div class="row">
<div class="control-group"><i class="fas fa-phone big-icon"></i>
<fieldset>
<input class="modal-input" type="tel" id="phone" placeholder=" " tabindex="3" required="required" data-validation-required-message="• Please enter your phone number." maxlength="10" minlength="9" pattern="[0-9]+" data-validation-pattern-message="Only numbers.">
<span class="inputup">Your Phone:</span>
</fieldset><p class="help-block text-danger text-center"></p>
</div>
</div>
<div class="row">
<div class="control-group"><i class="fas fa-pencil-alt big-icon align-top"></i>
<fieldset>
<textarea class="modal-input" id="message" placeholder=" " tabindex="4" required="required" data-validation-required-message="• Please enter a message." minlength="20" maxlength="1000"></textarea>
<span class="inputup">Type Your Message Here:</span>
</fieldset><p class="help-block text-danger text-center"></p>
</div>
</div>
<?php
if (empty($_SESSION['key']))
$_SESSION['key'] = bin2hex(random_bytes(32));
//create CSRF Token
$_SESSION['sec'] = hash_hmac('sha256', 'hi there : index.php', $_SESSION['key']);
?>
<input type="hidden" id="token" value="<?=$_SESSION['sec']?>">
<div class="row">
<div class="control-group">
<button id="sendMessageButton" class="button-temp button-temp2" type="submit" name="submit" tabindex="5" data-submit="...Sending">Send <i class="fas fa-paper-plane"></i>
</button>
<div id="success"></div>
</div>
</div>
<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
</form>
</div>
<div class="modal-footer justify-content-center">
<button class="button-temp button-temp3" data-dismiss="modal" aria-hidden="true">close</button>
</div>
</div>
</div>
</div>
<!-- jQuery library -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<!-- Popper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<!-- Latest compiled JavaScript -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<!-- jqBootstrapValidation -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqBootstrapValidation/1.3.6/jqBootstrapValidation.min.js"></script>
<script src="js/temp-contact_me.min.js"></script>
<script>
$(function(){
$('#modalContactForm').on('shown.bs.modal', function () {
$('#name').focus()
});
});
</script>
<script src="https://www.google.com/recaptcha/api.js?render=..." data-cfasync="false" async defer></script>
<script data-cfasync="false" async defer>
grecaptcha.ready(function() {
grecaptcha.execute('...', {
action: 'homepage'}).then(function(token) {
//console.log(token);
// add token value to form
document.getElementById('g-recaptcha-response').value = token;
});
});
</script>
</body>
</html>
temp-contact_me.js
$(function() {
$("#contactForm input,#contactForm textarea").jqBootstrapValidation({
preventSubmit: true,
submitError: function($form, event, errors) {
//alert('There was some error performing the AJAX call!');
// additional error messages or events
},
submitSuccess: function($form, event) {
event.preventDefault(); // prevent default submit behaviour
// get values from FORM
var name = $("input#name").val();
var email = $("input#email").val();
var phone = $("input#phone").val();
var message = $("textarea#message").val();
var token = $("input#token").val();
var captcha = $("input#g-recaptcha-response").val();
var firstName = name; // For Success/Failure Message
// Check for white space in name for Success/Fail message
if (firstName.indexOf(' ') >= 0) {
firstName = name.split(' ').slice(0, -1).join(' ');
}
$this = $("#sendMessageButton");
$this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages
$.ajax({
url: "././mail/temp-contact_me.php",
type: "POST",
data: {
name: name,
phone: phone,
email: email,
token: token,
captcha: captcha,
message: message
},
cache: false,
success: function(data) {
console.log(data)
// Success message
$('#success').html("<div class='alert alert-success'>");
$('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
.append("</button>");
$('#success > .alert-success')
.append("<strong>Your message has been sent. </strong>");
$('#success > .alert-success')
.append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
error: function() {
// Fail message
$('#success').html("<div class='alert alert-danger'>");
$('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
.append("</button>");
$('#success > .alert-danger').append($("<strong>").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"));
$('#success > .alert-danger').append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
complete: function() {
setTimeout(function() {
$this.prop("disabled", false); // Re-enable submit button when AJAX call is complete
}, 1000);
}
});
},
filter: function() {
return $(this).is(":visible");
},
});
$("a[data-toggle=\"tab\"]").click(function(e) {
e.preventDefault();
$(this).tab("show");
});
});
/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
$('#success').html('');
});
temp-contact_me.php
<?php
session_start();
// Only process POST reqeusts.
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Build POST request:
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = '...';
$recaptcha_ip = $_SERVER['REMOTE_ADDR'];
$recaptcha_response = $_POST['captcha'];
// Make and decode POST request
$recaptcha = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response . '&remoteip=' . $recaptcha_ip);
$recaptcha = json_decode($recaptcha);
if($recaptcha->success==true) {
// Take action based on the score returned:
if ($recaptcha->score >= 0.5) {
print_r("Verified - email sent"); // Verified - send email
//validate Token
if (hash_equals($_SESSION['sec'], $_POST['token'])) {
// Check for empty fields
if(empty($_POST['name']) || empty($_POST['email']) || empty($_POST['phone']) || empty($_POST['message'])
|| !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
echo "No arguments Provided!";
return false;
} else {
//sanitizing entries
function cleanInputs ($data)
{
trim($data);
strip_tags($data);
stripslashes($data);
htmlspecialchars($data);
htmlentities($data, ENT_QUOTES, 'UTF-8');
return ($data);
}
$name = cleanInputs($_POST['name']);
$email_address = cleanInputs($_POST['email']);
$phone = cleanInputs($_POST['phone']);
$message = cleanInputs($_POST['message']);
//check for header injections
function has_header_inject ($str)
{
return preg_match ("/[\r\n]/", $str );
}
if (has_header_inject($name) || has_header_inject($email_address) || has_header_inject($phone) || has_header_inject($message)) {
die (); //if true, kill the process-script
} else {
$to = '...';
$subject = 'Website Message From: ' .$name;
$headers = 'MIME-Version: 1.0' . "\r\n" .
'Content-type:text/html; charset=UTF-8' . "\r\n" .
'From: ' . $email_address . "\r\n" .
'Reply-To: ' . $email_address . "\r\n" .
'X-Priority: 2' . "\r\n" . //(values: 1 to 5- from the highest to lowest)
'MSMail-Priority: Normal' . "\r\n" . //(values: High, Normal, or Low)
'Importance: Normal' . "\r\n" . //(values: High, Normal, or Low)
'X-Mailer: PHP/' . phpversion();
$message = wordwrap($message, 80, '<br/>', true);
$email_body = "<html><body>
You have received a new message, here are the details:<br/><br>
<table rules='all' style='border-color: #666;' cellpadding='10'>
<tr style='letter-spacing:0.7px; color: white;'>
<td style='background: #18648F;'><b>Name:</b></td>
<td style='background: #23A895;'>" . $name . "</td>
</tr>
<tr style='letter-spacing:0.7px; color: white;'>
<td style='background: #18648F;'><b>Email:</b></td>
<td style='background: #23A895;'>" . $email_address . "</td>
</tr>
<tr style='letter-spacing:0.7px; color: white;'>
<td style='background: #18648F;'><b>Phone:</b></td>
<td style='background: #23A895;'>" . $phone . "</td>
</tr>
<tr style='letter-spacing:0.7px; color: white;'>
<td style='background: #18648F;'><b>Message:</b></td>
<td style='background: #23A895;'>" . $message . "</td>
</tr>
</table>
</body></html>";
// Send the email.
if (mail($to, $subject, $email_body, $headers)) {
http_response_code(200); // Set a 200 (okay) response code.
echo "Thank You! Your message has been sent.";
session_destroy();
} else {
http_response_code(500); // Set a 500 (internal server error) response code.
echo "Oops! Something went wrong and we couldn't send your message.";
die ();
}
}
}
} else {
echo "CSRF Token Failed!!";
die ();
}
} else {
print_r("Not verified - show form error"); // Not verified - show form error
exit;
}
} else { // there is an error, timeout-or-duplicate meaning you are submit the form
print_r($recaptcha);
exit;
}
} else {
// Not a POST request, set a 403 (forbidden) response code.
http_response_code(403);
echo "There was a problem with your submission, please try again.";
die ();
}
?>