Full Tutorial To Restrict or Block Ip Address To Prevent Hacking In Laravel

There may be malicious users using our applications on the web. These users may either annoy other visitors or want to crash the application using our forms (like SQL injection…). By detecting the ips of such visitors, we can block them at the application level. In this post, I will explain how to prevent malicious users by blacklisting their IPs in Laravel 8, which is the most used PHP framework in web applications.

Adding the last_login_at and last_login_ip Fields to the User Table

First, we will save the time information and ips of the users when they log in to our application in the database. Since the Laravel User model does not initially have these fields, we will add these fields to the User table. For this, we will first create the migration file. We create the migration file by running the following command in Laravel.

php artisan make:migration add_login_fields_to_users_table

class AddLoginFieldsToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->datetime('last_login_at')->nullable();
            $table->string('last_login_ip')->nullable();
        });
    }
}

We add last_login_at and last_login_ip fields to the migration file and run the php artisan migrate command in the terminal.

In the app\Models\User.php model php file, we define the fields we add to the database.

class User extends Authenticatable
{
  protected $fillable = [
        'name',
        'email',
        'password',
    	'last_login_at', 
    	'last_login_ip',
    ];

In the last step, we override the authenticated method in LoginController to save the ip and login time when the user logs into our web application.

app/Http/Controllers/Auth/LoginController.php

function authenticated(Request $request, $user)
{
    $user->update([
        'last_login_at' => Carbon::now()->toDateTimeString(),
        'last_login_ip' => $request->getClientIp()
    ]);
}

blocked_ips Table

Then we will create the blocked_ips table where the ips to be blocked will be kept. For this, we create the php artisan make:migration create_blocked_ips_table migration file and define the ip field in it.

class CreateBlockedIpsTable extends Migration
{
    public function up()
    {
        Schema::create('blocked_ips', function (Blueprint $table) {
            $table->id();
            $table->string('ip')->nullable();
            $table->timestamps();
        });
    }

php artisan migrate diyerek tablomuzu oluşturuyoruz.

We create our table by running php artisan migrate.

class BlockedIps extends Controller
{
    protected $fillable = [
        'ip'
    ];
}

We define routes in the routes/web.php file for the CRUD(create, read, update and delete) operations of this table.

		Route::get('blocked_ips', 'BlockedIpsController@getAll')->name('dashboard.blocked_ips');
		Route::get('blocked_ips', 'BlockedIpsController@create')->name('dashboard.create_blocked_ip');;
		Route::post('blocked_ips', 'BlockedIpsController@store')->name('dashboard.store_blocked_ip');;
		Route::delete('blocked_ips/{id}', 'BlockedIpsController@delete')->name('dashboard.delete_blocked_ip');;

We create our controller with the php artisan make:controller BlockedIps command. We write the methods we will use in it.

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class BlockedIpsController extends Controller
{
    public function getAll(Request $request){
      	try{
            $blocked_ips = \App\BlockedIp::get();
        }catch(\Exception $e){
            Log::error('Error when fetching blocked ips' . $e->getMessage());
        }
        return view('blocked_ips', $blocked_ips);
    }
  
  	public function create(Request $request){
        return view('create_blocked_ip');
    }

    public function store(Request $request){
        $params = $request->all();
      
        try{
            $blocked_ip = new \App\BlockedIps();
            $blocked_ip->fill($params);
            $blocked_ip->save();
        }catch(\Exception $e) {
            Log::error('Admin: new blocked ip error : ' . $e->getMessage());
        }
        return redirect()->route('dashboard.blocked_ips');
    }

    public function delete(Request $request, $id){
      	$blocked_ip = \App\BlockedIp::find($id);
 		try{
                $blocked_ip->delete();
           }catch(\Exception $e){
                Log::error('Admin: delete blocked ip error : ' . $e->getMessage());
           }
        return redirect()->route('dashboard.blocked_ips');
    }
}

After creating the methods in controller, we create our view files.

We list the ips in our blocked_ips.blade.php file.

<h2>Blocked IP list</h2>
    <a href="{{route('dashboard.create_blocked_ip')}}" class="btn btn-success" style="float: right;">Add IP</a>
    <table class="table table-hover">
        <thead>
            <tr>
                <th>IP</th>
                <th></th>
             </tr>
        </thead>
        <tbody>
             @for($blocked_ips as $blocked_ip)
                 <tr>
                     <td>{{$blocked_ip->ip}}</td>
                     <td><a href="{{route('dashboard.delete_blocked_ip', ['id' => $blocked_ip->id])}}" class="btn btn-danger delete-button">Delete</a></td>
                 </tr>
             @endfor
		</tbody>
	</table>

We blacklist the ip we want to block with the form in our add_blocked_ip.blade.php file.

<form action="" method="post" action="{{ route('dashboard.store_blocked_ip') }}">
      <!-- CROSS Site Request Forgery Protection -->
      @csrf
      <div class="form-group">
      	<label>Ip</label>
       	<input type="text" name="ip" required="required" class="form-control" placeholder="___.___.___.___">
      </div>
      <button type="submit" value="Submit" class="btn btn-dark btn-block">Save</button>
</form>

RestrictIpMiddleware

Now we create middleware to prevent the user whose ip we have detected in the database from entering the application. For this, we run the php artisan make:middleware RestrictIpMiddleware command on the terminal screen.

We add the RestrictIpAddressMiddleware middleware to the $middleware array in the kernel.php file to run it on every request.

protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    	\App\Http\Middleware\RestrictIpAddressMiddleware::class,
    ];

RestrictIpAddressMiddleware.php

We prevent unwanted users from using our application by overriding the handle method of middleware as follows.

<?php

namespace App\Http\Middleware;

use Closure;

class RestrictIpAddressMiddleware
{

    // Blocked IP addresses
    public $restrictedIp = [];
    
    public function restrictedIp(){
        $this->restrictedIp = \App\BlockedIps::get();
    }
    
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $this->restrictedIp();
        if (in_array($request->ip(), $this->restrictedIp->pluck('ip')->toArray())) {
            return response()->json(['message' => "You are not allowed to access this site."]);
        }
        return $next($request);
    }
}

Now we can detect the ip of unwanted users and keep them away from our app.

Good luck …