#StackBounty: #beginner #php #html Contact form with spam-prevention – follow-up

Bounty: 50

Background

The basics of this project are already covered here.

I got the suggestions to

  • don’t create the exercise with javascript.
  • use pictures instead of plaintext.

I tried to implement the suggestions like this:

The code

Example-form:

<form method="POST" action="../php/send.php" class='input'>
    <label>Your Name:</label><br>
    <input type="text" name="myName" placeholder="Name" required/><br><br>
    <label>Your Email:</label><br>
    <input type="text" name="myEmail" placeholder="E-Mail" required/><br><br>
    
    <!-- Honeypott -->
    <input type="text" id="website" name="website"/>
    <label>Message:</label><br>
    <textarea rows="8" name="myMessage" style='width: 100%; resize: none; border: 1px solid Gray; border-radius: 4px; box-sizing: border-box; padding: 10px 10px;' placeholder="Message" required></textarea><br><br>
    <input id='exerciseText' name='exerciseText', style='display: none;' value='
        <?php
        include '../php/randomExercise.php';
        $var = randText();
        echo $var;
        ?>'>
    </input>

    <label id='exercise'>
        
        <?php
        echo randExer($var);
        ?>
    </label><br>
    <input type='number' id='solution' name='solution' placeholder="Solution" required/>
    <div style='display: inline-block; text-align: left;'>
        <input type="checkbox" id="consent" name="consent" value="consent" required="">
        <label>I agree with saving and sending this message according to the privacy policy.
        </label>
    </div>
    <input style='' type="submit" value="Send"/>
</form>

randomExercise.php:

<?php

    $encryptionPassword = "***";

    function randExer($rand) {

        //========================
        //Change for customization
        //========================

        //First of all:
        //Please change the $encryptionPassword above (16 chars)

        //Width of the created image
        $width = 200;

        //Height of the created image
        $height = 50;

        //RGB values for the text on the black image
        $textColorRed = 255;
        $textColorGreen = 255;
        $textColorBlue = 255;

        //RGB values of the random lines on the image
        $linesRed = 192;
        $linesGreen = 192;
        $linesBlue = 192;

        //Value between 1 and 5
        $fontSize = 5;  

        //Coordinates where the text starts
        $upperLeftCornerX = 18;
        $upperLeftCornerY = 18;

        //Text will be rotated by $angle-degrees
        $angle = 10;

        global $encryptionPassword;


        //=============================================
        //From here no changes needed for customization
        //=============================================

        $random = openssl_decrypt($rand,"AES-128-ECB", $encryptionPassword);
        
        //Creates a black picture
        $img = imagecreatetruecolor($width, $height);

        //uses RGB-values to create a useable color
        $textColor = imagecolorallocate($img, $textColorRed, $textColorGreen, $textColorBlue);
        $linesColor = imagecolorallocate($img, $linesRed, $linesGreen, $linesBlue);

        //Adds text
        imagestring($img, $fontSize, $upperLeftCornerX, $upperLeftCornerY, $random . " = ?", $textColor);

        //Adds random lines to the images
        for($i = 0; $i < 5; $i++) {
            imagesetthickness($img, rand(1, 3));
            $x1 = rand(0, $width / 2);
            $y1 = rand(0, $height / 2);
            $x2 = $x1 + rand(0, $width / 2);
            $y2 = $y1 + rand(0, $height / 2);
            imageline($img, $x1, $x2, $x2, $y2, $linesColor);
        }

        $rotate = imagerotate($img, $angle, 0);

        //Attribution: https://stackoverflow.com/a/22266437/13634030
        ob_start();
            imagejpeg($rotate);
            $contents = ob_get_contents();
        ob_end_clean();
        $imageData = base64_encode($contents);
        $src = 'data:'. mime_content_type($contents) . ';base64,' . $imageData;
        return '<img alt="" src="' . $src . '">';
    };

    function randText() {
        global $encryptionPassword;

        //Creating random (simple) math problem
        $arr = array("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten");
        $item1 = $arr[array_rand($arr)];
        $item2 = $arr[array_rand($arr)];
        $random = $item1 . " + " . $item2;
        $encrypted = openssl_encrypt($random,"AES-128-ECB", $encryptionPassword);
        return $encrypted;
    }
?>

send.php

<?php
    
    //Get simple math-problem (e.g. four + six)
    $str = openssl_decrypt($_REQUEST['exerciseText'], "AES-128-ECB", "***");
    
    $first = strpos($str, " ");

    //Get first number (e.g. four)
    $substr1 = substr($str, 0, $first);

    //Get second number (e.g. six)
    $substr2 = substr($str, $first + 3, strlen($str) - $first - 3);
    $arr = array("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten");
    /*
     * Convertring strings to numbers, e.g.
     * four -> 4
     * six  -> 6
     */

    $x = 0;
    $y = 0;

    for($i = 0; $i <= 10; $i++) {
        if(strcmp($substr1, $arr[$i]) == 0) {
            $x = $i;
            break;
        }
    }

    for($i = 0; $i <= 10; $i++) {
        if(strcmp($substr2, $arr[$i]) == 0) {
            $y = $i;
            break;
        }
    }

    $z = intval($_POST['solution']);

    //Did user enter right solution?
    if($z == ($x + $y)) {
        
        //Bot filled the honeypott-tree
        if(!empty($_POST['website'])) {
            header("Location:/sites/messageError.html");
            die();
        }

        $userName = $_POST['myName'];
        $userEmail = $_POST['myEmail'];
        $userMessage = $_POST['myMessage'];

        //Did user enter a valid email-adress?
        if(!filter_var($userEmail, FILTER_VALIDATE_EMAIL)) {
            header("Location:http:///sites/messageError.html");
            die();
        }

        //Creating message
        $to = "***";
        $subject = "New Contact-form message";
        $body = "Content:";

        $body .= "nn Name: " . $userName;
        $body .= "nn Email: " . $userEmail;
        $body .= "nn Message: " . $userMessage;

        //Trying to send message
        if(mail($to, $subject, $body)){
            header("/sites/message.html");
            die();
        } else{
            header("Location:/sites/messageError.html");
            die();
        }
    }

    header("Location:/sites/messageError.html");
?>

Questions

All suggestions are welcome, but I am especially interested in the security of this approach and how to further improve it.


Edit: I just created a git-repository and a working demo. Maybe that’s helpful for review.


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.