Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

PHP

Pieter Bracke
Pieter Bracke
4,078 Points

problem with CSRF protection on form

Hi,

I am having an issue with implementing CSRF protection into my form. To be more precise when the check happens to see if the token matches I would like to create an if statement e.g

 if(check = ok){ 
execute the remainder of the php script i am using to send the form data
} else { 
display an error message and don't send the form data
}

This is the class I am using for obtaining the CSRF code:

<?php
/**
 * NoCSRF, an anti CSRF token generation/checking class.
 *
 * Copyright (c) 2011 Thibaut Despoulain <http://bkcore.com/blog/code/nocsrf-php-class.html>
 * Licensed under the MIT license <http://www.opensource.org/licenses/mit-license.php>
 *
 * @author Thibaut Despoulain <http://bkcore.com>
 * @version 1.0
 */
class NoCSRF
{

    protected static $doOriginCheck = false;

    /**
     * Check CSRF tokens match between session and $origin. 
     * Make sure you generated a token in the form before checking it.
     *
     * @param String $key The session and $origin key where to find the token.
     * @param Mixed $origin The object/associative array to retreive the token data from (usually $_POST).
     * @param Boolean $throwException (Facultative) TRUE to throw exception on check fail, FALSE or default to return false.
     * @param Integer $timespan (Facultative) Makes the token expire after $timespan seconds. (null = never)
     * @param Boolean $multiple (Facultative) Makes the token reusable and not one-time. (Useful for ajax-heavy requests).
     * 
     * @return Boolean Returns FALSE if a CSRF attack is detected, TRUE otherwise.
     */
    public static function check( $key, $origin, $throwException=false, $timespan=null, $multiple=false )
    {
        if ( !isset( $_SESSION[ 'csrf_' . $key ] ) )
            if($throwException)
                throw new Exception( 'Missing CSRF session token.' );
            else
                return false;

        if ( !isset( $origin[ $key ] ) )
            if($throwException)
                throw new Exception( 'Missing CSRF form token.' );
            else
                return false;

        // Get valid token from session
        $hash = $_SESSION[ 'csrf_' . $key ];

        // Free up session token for one-time CSRF token usage.
        if(!$multiple)
            $_SESSION[ 'csrf_' . $key ] = null;

        // Origin checks
        if( self::$doOriginCheck && sha1( $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) != substr( base64_decode( $hash ), 10, 40 ) )
        {
            if($throwException)
                throw new Exception( 'Form origin does not match token origin.' );
            else
                return false;
        }

        // Check if session token matches form token
        if ( $origin[ $key ] != $hash )
            if($throwException)
                throw new Exception( 'Invalid CSRF token.' );
            else
                return false;

        // Check for token expiration
        if ( $timespan != null && is_int( $timespan ) && intval( substr( base64_decode( $hash ), 0, 10 ) ) + $timespan < time() )
            if($throwException)
                throw new Exception( 'CSRF token has expired.' );
            else
                return false;

        return true;
    }

    /**
     * Adds extra useragent and remote_addr checks to CSRF protections.
     */
    public static function enableOriginCheck()
    {
        self::$doOriginCheck = true;
    }

    /**
     * CSRF token generation method. After generating the token, put it inside a hidden form field named $key.
     *
     * @param String $key The session key where the token will be stored. (Will also be the name of the hidden field name)
     * @return String The generated, base64 encoded token.
     */
    public static function generate( $key )
    {
        $extra = self::$doOriginCheck ? sha1( $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) : '';
        // token generation (basically base64_encode any random complex string, time() is used for token expiration) 
        $token = base64_encode( time() . $extra . self::randomString( 32 ) );
        // store the one-time token in session
        $_SESSION[ 'csrf_' . $key ] = $token;

        return $token;
    }

    /**
     * Generates a random string of given $length.
     *
     * @param Integer $length The string length.
     * @return String The randomly generated string.
     */
    protected static function randomString( $length )
    {
        $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijqlmnopqrtsuvwxyz0123456789';
        $max = strlen( $seed ) - 1;

        $string = '';
        for ( $i = 0; $i < $length; ++$i )
            $string .= $seed{intval( mt_rand( 0.0, $max ) )};

        return $string;
    }

}
?>

This is my code i am using in my page where the form is located:

<?php
session_start();
include('includes/nocsrf.php');

if(isset($_POST['first-name'], 
         $_POST['last-name'], 
         $_POST['email'],
         $_POST['bericht'])){
    try
    {
        // Run CSRF check, on POST data, in exception mode, for 10 minutes, in one-time mode.
        NoCSRF::check( 'csrf_token', $_POST, false, 60*10, false );
        // form parsing, DB inserts, etc.
        // ...
        $result = 'CSRF check passed. Form parsed.';
    }
    catch ( Exception $e )
    {
        // CSRF attack detected
        echo 'Attack averted';
        exit;
    }

}

    else
        {
             $result = '';
        }

        // Generate CSRF token to use in form hidden field
        $token = NoCSRF::generate( 'csrf_token' );
?>

and this is my code from my script which sends my form-data:

<?php
session_start();
if ($_SERVER["REQUEST_METHOD"] == "POST"){

$firstname =  trim(filter_input(INPUT_POST,"first-name",FILTER_SANITIZE_STRING));
$lastname = trim(filter_input(INPUT_POST,"last-name",FILTER_SANITIZE_STRING));
$email = trim(filter_input(INPUT_POST,"email",FILTER_SANITIZE_EMAIL));
$bericht = trim(filter_input(INPUT_POST,"bericht",FILTER_SANITIZE_SPECIAL_CHARS));

if ($firstname == "" || $lastname == "" || $email == "" || $bericht == "") {
    echo "<H1 style='font-size:40px;color:red;'>Gelieve alle velden in te vullen!</H1>";
    echo " </br>";
    echo "<a href='../contact.php'><button style='width:200px;height:40px;'>terug naar contact form</button></a>";
    exit;
}

if ($_POST["adress"] != "") {
    echo "Bad form input";
    echo "<a href='../contact.php'><button>terug naar contact form</button></a>";
    exit;
}

require("../includes/phpmailer/class.phpmailer.php");

$mail = new PHPMailer;

if  (!$mail->ValidateAddress($email)){
    echo "<H1 style='font-size:40px;color:red'>Gelieve een geldig email adres in te geven.</H1>";
    echo " </br>";
    echo "<a href='../contact.php'><button style='width:200px;height:40px;'>terug naar contact form</button></a>";
    exit;
}



$email_body = "";
$email_body .= "first-name: " . $firstname . "\n";
$email_body .= "last-name: " . $lastname . "\n";
$email_body .= "email: " . $email . "\n";
$email_body .= "bericht: " . $bericht . "\n";

$mail->setFrom('$email', '$firstname', '$lastname');
$mail->addAddress('gin0boxbelgium@gmail.com', 'Gin0°-Box');     // Add a recipient

$mail->isHTML(false);                                  // Set email format to HTML

$mail->Subject = 'Gin0°-Box contact form' . $name;
$mail->Body    = $email_body;

if(!$mail->send()) {
    echo 'Message could not be sent.';
    echo 'Mailer Error: ' . $mail->ErrorInfo;
    exit;
} 


header("location:../thanks.php");

}

?>

I don't quite understand how I am supposed to make this if statement work in relation to the other pieces of code some help would be greatly appreciated because I have been trying for hours and I can't seem to find it...

Thx in advance