HONEYPOTS — AN EXAMPLE WITH LARAVEL

Uğur Müslim
3 min readDec 12, 2020

--

Bots are everywhere. Most of the times we see captchas in login pages. But there are some other ways too and one of them is Honeypot. Basically you put some hidden inputs and if they are filled, you know that it is a computer. As always you are setting a trap for bots.

So in this section I’ll show you an example in Laravel. First let’s create a laravel project;

laravel new honeypot

And I will create a ArticleController.

php artisan make:controller ArticleController

Let’s fill controller

<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class ArticleController extends Controller
{
public function create()
{
return view(‘articles.create’);
}
}

Lets modify routes in web.php;

Route::get(‘article’, ‘App\Http\Controllers\ArticleController@create’);
Route::post(‘article’, ‘App\Http\Controllers\ArticleController@store’);

And I will create these folders and filesin resources.

And the code in articles/create.blade.php;

<div class="container">
<form method="post" action="/article" enctype="multipart/form-data">
{{ csrf_field() }}
<div class="form-group row">
<label for="title" class="col-sm-3 col-form-label">Article Title</label>
<div class="col-sm-9">
<input name="title" type="text" class="form-control" id="title" placeholder="Article Title">
</div>
</div>
<div class="form-group row">
<label for="articleText" class="col-sm-3 col-form-label">Article Text</label>
<div class="col-sm-9">
<textarea name="articleText" type="text" class="form-control" id="articleText"
placeholder="Article"></textarea>
</div>
</div>
<div class="form-group row">
<label for="userName" class="col-sm-3 col-form-label">User Name</label>
<div class="col-sm-9">
<input name="userName" type="text" class="form-control" id="userName"
placeholder="userName">
</div>
</div>
<div class="form-group row">
<label for="requestTime" class="col-sm-3 col-form-label">Request Time</label>
<div class="col-sm-9">
<input name="requestTime" type="text" class="form-control" id="requestTime"
placeholder="Requesttime" value="{{time()}}">
</div>
</div>
<div class="form-group row">
<div class="offset-sm-3 col-sm-9">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>

The page will look like this;

User Name and Request Time will be hidden at the end.

Let’s write a store function in ArticleController.

public function store()
{
// Some Validation
if (!request()->has(‘userName’) || request()->input(‘userName’)) {
abort(422, ‘Catched by username’);
}
if (time() — request()->input(‘requestTime’) <= 2) {
abort(422, ‘Catched by requestTime’);
}
return ‘Everything is ok’;
}

So let’s examine this function;

  • We are checking if request has userName input. If not there is a problem.
  • If userName input is filled, there is a problem.
  • requestTime input’s value is time(). And with that input we are calculating the time between pages initialization and post. So if it is submitted so fast like under 2 seconds there is a problem.

Don’t forget, we will make these two inputs hidden so users won’t see them only bots will see and if they are not clever enough they won’t be able to pass this test.

But why stop here. Let’s create a middleware and make our code clean.

php artisan make:middleware Honeypot

We will copy the check codes and paste it in middleware. And erase them from store function.

public function handle(Request $request, Closure $next)
{
if (!$request->has(‘userName’) || $request->input(‘userName’)) {
abort(422, ‘Catched by username’);
}
if (time() — $request->input(‘requestTime’) <= 2) {
abort(422, ‘Catched by requestTime’);
}
return $next($request);
}

And before we forgot we must define middleware to our route.

Route::post(‘article’, ‘App\Http\Controllers\ArticleController@store’)->middleware(Honeypot::class);

So when we make the two inputs hidden honeypot will be ready.

<div class=”form-group row”>
<label for=”userName” class=”col-sm-3 col-form-label”>User Name</label>
<div class=”col-sm-9">
<input name=”userName” type=”text” class=”form-control” id=”userName”
placeholder=”userName” hidden>
</div>
</div>
<div class=”form-group row”>
<label for=”requestTime” class=”col-sm-3 col-form-label”>Request Time</label>
<div class=”col-sm-9">
<input name=”requestTime” type=”text” class=”form-control” id=”requestTime”
placeholder=”Requesttime” value=”{{time()}}” hidden>
</div>
</div>

Actually we can continue with this article. There are some basic things we must do but I am leaving these to you. Like;

  • We must not use constant number in time difference check. You can make it configurable.
  • You can convert your html form in to reusable components or includes.

This is it. As always I ll put a Laracast link below. I learned it from there. And watching it is better I think. And it shows some other features too.

--

--

Uğur Müslim
Uğur Müslim

Written by Uğur Müslim

Software developer, guitar player, drinks mostly tea and water. Lately reads history books. Trying to get away from fiction.