Securing Your Laravel Application Smartly With UUID
Heyo! We will be learning a smart way of securing our Laravel Application and making it difficult to be hacked.
UUID will help us achieve this.
I am sure you are thinking about what UUID means. It simply means Universal Unique Identifier and what it does, basically, is prevent us from exposing our ID to the world. The UUID has 36 characters and is very difficult to get into by the black hats.
In this tutorial, we will be learning how to work with UUIDs with a simple directory list appilcation that stores the names of dogs.
Let's get started.
Requirements
- Laravel Installed (5.5.*)
- PHP (>=7.1)
- Text Editor You Love
Setup Application
We will start by creating a Laravel application with the command below:
laravel new dogApp
or you can use:
composer create-project --prefer-dist laravel/laravel
After doing this, you should fire up your text editor and let us get our hands dirty.
Model and Migrations
At this point, it is important we create models and migrations for our directory application. First, we will create a model and migration file for storing the names of the dogs. Run the command below:
php artisan make:model Dog -m
After doing this, go to your database/migrations
folder and open the migration file we just created. Mine is called 2017_12_06_011644_create_dogs_table.php
.
Add columns to the table to be created. Let your up
method look like the one below:
public function up()
{
Schema::create('dogs', function (Blueprint $table) {
$table->increments('id');
$table->string('dog_name');
$table->timestamps();
});
}
After doing that, go to your .env
and make sure your database configuration is perfect. Then, run the command below.
php artisan migrate
That command will create tables in your database.
Working With Controllers
Now, to speed this tutorial up, we will create a controller called DogController.php
and then we will create methods that handle adding new dog names, listing the dog names in the database, and also, editing the dog.
First, run php artisan make:controller DogController
to create the controller file, then go to the app/Http/Controllers
directory to open DogController.php
.
Finally, in this section, make sure your controller looks like the one below:
<?php
namespace App\Http\Controllers;
use App\Dog;
use Illuminate\Http\Request;
class DogController extends Controller
{
public function create()
{
return view('dog.new');
}
public function store(Request $request)
{
$dog = new Dog();
$dog->dog_name = $request->name;
if ($dog->save()) {
return redirect()->back()->with('success','Dog with name: '.$dog->dog_name.' was added successfully');
} else {
return redirect()->back()->with('error','Sorry! We apologize for this error');
}
}
public function show()
{
$dogs = Dog::all();
return view('dog.list',['dogs' => $dogs]);
}
public function edit($id)
{
$dog = Dog::find($id);
return view('dog.edit',['dog' => $dog]);
}
public function update(Request $request,$id)
{
$dog = Dog::find($id);
$dog->dog_name = $request->name;
if ($dog->save()) {
return redirect()->back()->with('success','Dog with name: '.$dog->dog_name.' was updated successfully');
} else {
return redirect()->back()->with('error','Sorry! We apologize for this error');
}
}
}
Routing
Before creating the views, let us create routes. Go to the routes
folder and open web.php
file. Erase everything you have in the file and add the following below:
<?php
Route::get('/', [
'uses' => 'DogController@create'
]);
Route::post('/create/', [
'uses' => 'DogController@store'
]);
Route::get('/list', [
'uses' => 'DogController@show'
]);
Route::get('/edit/{id}', [
'uses' => 'DogController@edit'
]);
Route::post('/update/{id}', [
'uses' => 'DogController@update'
]);
Views
We will be creating a series of views, but I would make it a lot easier. Just navigate to the resources/views
directory then we'll start creating views for our app.
- Create the
layouts
folder, then create themaster.blade.php
file in thelayouts
folder and add the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Dog Directory List APp">
<meta name="author" content="goodnesskay">
<link rel="icon" href="favicon.ico">
<title>Dog Directory List App</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div style="margin-top:50px;" class="container">
<div class="row">
@include('partials.message')
@yield('content')
</div>
</div>
</body>
</html>
- Create
dog
folder, then createnew.blade.php
,list.blade.php
, andedit.blade.php
files indog
folder.
- Add the code below to
new.blade.php
file:
@extends('layouts.master')
@section('content')
<div class="col-md-8">
<form action="{{ url('create') }}" method="post">
{{ csrf_field() }}
<h2 class="text-center">Create New Dog</h2><br>
<label for="dogName" class="sr-only">Dog's Name</label>
<input type="text" class="form-control" name="name" placeholder="Dog Name" required autofocus>
<br>
<button class="btn btn-lg btn-primary btn-block" type="submit">Create</button>
</form>
<hr>
<div class="text-center">
<a href="{{ url('list') }}" class="btn btn-success">View Dog List</a>
</div>
</div>
@endsection
- Also, add the code below to
list.blade.php
file:
@extends('layouts.master')
@section('content')
<div class="col-md-12">
<div class="panel panel-default panel-table">
<div class="panel-heading">
<div class="row">
<div class="col col-xs-6">
<h3 class="panel-title">Panel Heading</h3>
</div>
<div class="col col-xs-6 text-right">
<a href="{{ url('/') }}" class="btn btn-sm btn-primary btn-create">Add New</a>
</div>
</div>
</div>
<div class="panel-body">
<table class="table table-striped table-bordered table-list">
<thead>
<tr>
<th class="hidden-xs">#</th>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach($dogs as $dog)
<tr>
<td class="hidden-xs">#</td>
<td>{{ ucfirst($dog->dog_name) }}</td>
<td><a href="{{ url('edit',['id' => $dog->id ]) }}">Edit</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endsection
- Then, add the code below to
edit.blade.php
file:
@extends('layouts.master')
@section('content')
<div class="col-md-8">
<form action="{{ url('update',['id' => $dog->id ]) }}" method="post">
{{ csrf_field() }}
<h2 class="text-center">Edit Dog Record With Name "{{ $dog->dog_name }}" </h2><br>
<label for="dogName" class="sr-only">Dog's Name</label>
<input type="text" class="form-control" name="name" value="{{ $dog->dog_name }}" placeholder="Dog Name" required autofocus>
<br>
<button class="btn btn-lg btn-primary btn-block" type="submit">Update</button>
</form>
<hr>
<div class="text-center">
<a href="{{ url('list') }}" class="btn btn-success">View Dog List</a>
</div>
</div>
@endsection
- Create the
partials
folder, then createmessage.blade.php
file in thepartials
folder for alert messages once requests are made. Add the following to the file:
<div class="container">
@if (session('success'))
<div class="notice notice-success">
<strong>Success! </strong>{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="notice notice-success">
<strong>Error! </strong>{{ session('error') }}
</div>
@endif
</div>
<style type="text/css">
.notice {
padding: 15px;
background-color: #fafafa;
border-left: 6px solid #7f7f84;
margin-bottom: 10px;
-webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);
box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);
}
.notice-success {
border-color: #80D651;
}
.notice-success>strong {
color: #80D651;
}
.notice-danger {
border-color: #d73814;
}
.notice-danger>strong {
color: #d73814;
}
</style>
Now, The Problem
The problem in this application is simple. From the URL, I can access any data from the database via id
and any anonymous user can make changes to it. How do we solve this?
Install Package and Configure
We will be working with a package that simply helps us create UUIDs with ease. You can find the package here. Install the package by running the command below:
composer require webpatser/laravel-uuid
- Open
config/app.php
file and add'Uuid' => Webpatser\Uuid\Uuid::class,
to thealiases
array.
Update Migration
Now, we have to make changes to our migration file so it can cater to the UUIDs we want to create.
Open your migration file and replace $table->increments('id');
with the below:
$table->uuid('id');
$table->primary('id');
After doing this, go ahead to run php artisan migrate:refresh
to reset and re-run all migrations.
Create Trait
Now, we have to create a trait file that handles the creation of UUIDs once the event for creating a new resource is triggered. Go to app
folder and create a PHP traits file called Uuids.php
, then add the code below:
namespace App;
use Webpatser\Uuid\Uuid;
trait Uuids
{
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->{$model->getKeyName()} = Uuid::generate()->string;
});
}
}
Updating Models
We need to update our models to carry out the action we want it to carry. We will add the following to the body of our class in app/Dog.php
.
use Uuids;
/**
* Set auto-increment to false.
*
* @var bool
*/
public $incrementing = false;
What we did here is to use the trait file we created and set auto-incrementing to false.
Run Your More Secured App
Now, run your app with php artisan serve
. Fire up your browser and test your application. Go to the edit page of the resource you created and check the URL. Yippee! We have our UUID working and it is now difficult for a user without access to the page to update.