2
\$\begingroup\$

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">&times;</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'>&times;")
        .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'>&times;")
        .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 ();
}
?>
\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.