Search code examples
phpformscaptchaspam

Adding a form field add with a mathematical question to stop bots? :(


Good day, I'm looking for assisting on this. On my PHP page, I'm looking to add a simple mathematical question (ex: 5+2) to stop the spam. Any recommendation please? Would be highly appreciated, also feel free if there's way I can improve my code.

Here is my PHP page and form fields:

<div class="col-xs-12 col-md-8">
            <?php
            // Contact Form
            function contact_form() {
                if(isset($_POST["name"]) && $_POST["name"] != "")
                {
                    if(!filter_var($_POST["mail"], FILTER_VALIDATE_EMAIL)) {
                        echo "<div class='error'>The email address is not valid</div>";
                        return;
                    }

                    if($_POST["name"] == "" || $_POST["mail"] == "" || $_POST["message"] == "") {
                        echo "<div class='error'>Informations are missing</div>";
                        return;
                    }

                    $name = htmlentities($_POST["name"]);
                    $message = htmlentities($_POST["message"]);
                    $message = wordwrap($message, 70, "\r\n");

                    $to      = '@hotmail.com';
                    $subject = 'Message of '.$name.'';
                    $headers = 'From: ' . "\r\n" .
                        'X-Mailer: PHP/' . phpversion();
                    $message_final = "Someone sent you a message \nNom : ".$name."\nCourriel : ".$_POST["mail"]."\n\n\n".$message;

                    if(mail($to, $subject, $message_final, $headers)) {
                        echo "<div class='success'>Your message was sent. Thank you!</div>";
                        return;
                    } else {
                        echo "<div class='error'>There was an error. You message was not sent</div>";
                        return;
                    }

                }
            }
            contact_form();
            ?>

            <form method="post">
                <div class="form-group">
                    <label for="name">Name</label>
                    <input type="text" class="form-control" id="name" name="name" placeholder="Enter your name" required>
                </div>
                <div class="form-group">
                    <label for="name">Email</label>
                    <input type="email" class="form-control" id="mail" name="mail" placeholder="Enter your email" required>
                </div>
                <div class="form-group">
                    <label for="message">Message</label>
                    <textarea id="message" name="message" class="form-control" rows="3" required></textarea>
                </div>
                <div class="form-group">
                    <button type="submit" class="btn btn-default">Send</button>
                </div>
            </form>
        </div>

Cheers


Solution

  • I actually do something just like what you've described. There are varying ways to implement this, but an easy way is use a function that generates a random math problem, hashes the answer, and inserts the hash into a hidden field. The user submits the form, and you compare the hash of the answer submitted with the hash in the hidden field. You may also want to consider using unique tokens for forms so forms can't POST to your site without getting a token first.

    You can try something like this:

    function mathQuestion($lo, $hi) {
        $a = mt_rand($lo, $hi);
        $b = mt_rand($lo, $hi);
        $c = $a + $b;
        $hash = md5($c);
        $q = "$a + $b";
        return array($q, $hash);
    }
    function mathQuestionVerify($c, $hash) {
        return (md5($c) === $hash);
    }
    

    Then, in your form, something like:

    $mathQ = mathQuestion(10, 89);
    $mathText = $mathQ[0];
    $mathHash = $mathQ[1];
    echo "<div><label for='mathquestion'>What is $mathText?</label><input id='mathquestion' name='mathquestion'></div>";
    echo "<input type='hidden' name='mathanswer' value='$mathHash'>";
    echo "<input type='hidden' name='token' value='$token'>";
    

    The two fields above are the math related ones. The token variable here is a CSRF token in a form I grabbed this from. Actually, this is just a unique form token as I described earlier - I also use CSRF tokens when users are logged in.

    In the example above, when the form loads, we add two numbers each of which are between 10 and 89. You can change these to whatever you want. I use a wider range of numbers for less common tasks, like password resets, since that's not something that's done frequently. For a general contact form you should probably keep these numbers small - probably under 50.

    Note that md5 is not cryptographically secure. Neither is sha1. I don't actually think mt_rand() is either. Just throwing this out there. I use md5 in this case because we're not dealing with sensitive data and so a plain, fast hashing function works well for my purposes.

    I don't use this mechanism as my sole method of validation, but it can certainly slow down malicious attempts. If you want to go further, you could even use a function to rewrite the numeric question into text to make it harder for bots to spam.

    For your specific situation, you might try something like this:

    <div class="col-xs-12 col-md-8">
            <?php
            // Contact Form
            function contact_form() {
                if(isset($_POST["name"]) && $_POST["name"] != "")
                {
                    if(!isSet($_POST['mathanswer'] || !mathQuestionVerify($_POST['mathanswer'], $_POST['mathhash'])) {
                        echo "<div class='error'>Incorrect answer.</div>";
                        return;
                    }
                    if(!filter_var($_POST["mail"], FILTER_VALIDATE_EMAIL)) {
                        echo "<div class='error'>The email address is not valid</div>";
                        return;
                    }
    
                    if($_POST["name"] == "" || $_POST["mail"] == "" || $_POST["message"] == "") {
                        echo "<div class='error'>Informations are missing</div>";
                        return;
                    }
    
                    $name = htmlentities($_POST["name"]);
                    $message = htmlentities($_POST["message"]);
                    $message = wordwrap($message, 70, "\r\n");
    
                    $to      = '@hotmail.com';
                    $subject = 'Message of '.$name.'';
                    $headers = 'From: ' . "\r\n" .
                        'X-Mailer: PHP/' . phpversion();
                    $message_final = "Someone sent you a message \nNom : ".$name."\nCourriel : ".$_POST["mail"]."\n\n\n".$message;
    
                    if(mail($to, $subject, $message_final, $headers)) {
                        echo "<div class='success'>Your message was sent. Thank you!</div>";
                        return;
                    } else {
                        echo "<div class='error'>There was an error. You message was not sent</div>";
                        return;
                    }
    
                }
            }
            contact_form();
            ?>
    
            <form method="post">
                <div class="form-group">
                    <label for="name">Name</label>
                    <input type="text" class="form-control" id="name" name="name" placeholder="Enter your name" required>
                </div>
                <div class="form-group">
                    <label for="name">Email</label>
                    <input type="email" class="form-control" id="mail" name="mail" placeholder="Enter your email" required>
                </div>
                <div class="form-group">
                    <label for="message">Message</label>
                    <textarea id="message" name="message" class="form-control" rows="3" required></textarea>
                </div>
                <div class="form-group">
                    <button type="submit" class="btn btn-default">Send</button>
                </div>
    <?php
    $mathQ = mathQuestion(5, 15); # ask user to add 2 random numbers between 5 & 15
    $mathText = $mathQ[0];
    $mathHash = $mathQ[1];
    echo "<div><label for='mathquestion'>What is $mathText?</label><input id='mathquestion' name='mathquestion'></div>";
    echo "<input type='hidden' name='mathanswer' value='$mathHash'>";
    ?>
    
            </form>
        </div>