<< Blog

Simple Human Verification Using Laravel 5

Curran Jensen | Sep 29, 2014 (3 years ago)


If you need to secure a publicly available form such as a contact form, I have a simple idea for you: make the visitor prove they are a human instead of a spam bot!

The idea is for the visitor to answer an easy math question that is randomly generated, and then harness Laravel 5's awesome new FormRequest class, to make sure the answer is correct before proceeding with the rest of the form.

We'll start with the GET request that is responsible for returning the view for your form:

<?php namespace Curran\Http\Controllers;

class PagesController extends Controller {

    /**
     * displays contact page
     *
     * @return \Illuminate\View\View
     */
    public function contact()
    {
        $randomInteger1 = substr(mt_rand(),0,2);
        $randomInteger2 = substr(mt_rand(),0,1);

        $captchaQuestion = sprintf('What is %s + %s?', $randomInteger1, $randomInteger2);
        $captchaAnswer = $randomInteger1 + $randomInteger2;

        Session::put('captcha_answer', $captchaAnswer);

        return view('pages.contact', compact('captchaQuestion'));
    }
}

What this does is generate some random numbers (a two-digit, and a one-digit number). The correct answer is determined and stored in the $_SESSION. The question is also generated as a string that you may pass to your form view:

<div class="form-group {!! $errors->first('human', 'has-error has-feedback') !!}">
    {!! Form::label('human', 'Human verification question: ' . $captchaQuestion) !!}
    {!! Form::text('human', null, ['placeholder' => $captchaQuestion, 'class' => 'form-control', 'required']) !!}
    @if($errors->has('human'))
        <span class="glyphicon glyphicon-remove form-control-feedback"></span>
        {!! $errors->first('human', '<span class="help-block">:message</span>') !!}
    @endif
</div>

Next, generate a class that will validate the form:

php artisan make:request ContactFormRequest

Setup this class along these lines (your namespace will be different, of course):

<?php namespace Curran\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ContactFormRequest extends FormRequest {

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
              'name' => 'required',
              'email' => 'required|email',
              'subject' => 'required',
              'body' => 'required',
              'human' => 'required|numeric|in:' . \Session::get('captcha_answer'),
          ];
    }

    /**
     * Set custom messages for validator errors.
     *
     * @return array
     */
    public function messages()
    {
        return [
            'human.required' => 'Please verify you are human by answering this simple math question.',
            'human.numeric' => 'Please enter only numbers.',
            'human.in' => 'Sorry, your answer is wrong. Please try again.',
        ];
    }

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }
}  

Notice we added some custom error messages that you can take advantage of in your form view.

Lastly, the real magic of this is that if you type-hint this class in the controller method that processes the form, the method will only be hit if the form (and the math answer) is valid, and a real human! Then you can send an email or do whatever you need to do.

<?php namespace Curran\Http\Controllers;

class PagesController extends Controller {

   /**
    * sends info from contact page via email
    *
    * @param ContactFormRequest $contactFormRequest
    * @return \Illuminate\Http\RedirectResponse
    */
    public function contactSend(ContactFormRequest $contactFormRequest)
    {
        $email = $contactFormRequest->email;
        $name = $contactFormRequest->name;
        $body = $contactFormRequest->body;
        $subject = $contactFormRequest->subject;

        $to = Config::get('site.email');
        $owner = Config::get('site.owner');

        Mail::queue('emails.contact', ['name' => $name, 'email' => $email, 'body' => $body], function($message) use ($to, $owner, $subject, $email, $name)
        {
            $message->to($to, $owner)->subject($subject)->replyTo($email, $name);
        });

        Flash::overlay('Thanks for contacting me, ' . $name . '. You\'ll hear from me soon!', 'Your message was sent!');

        return \Redirect::route('contact');
    }        
}

Thanks for reading!


2896 | Laravel 5

comments powered by Disqus