DDoS prevention middleware and recaptcha validation rules added. Still need to implement recaptcha into the existing auth views.
This commit is contained in:
parent
608b071ee8
commit
f8407994cc
|
|
@ -9,6 +9,9 @@ LOG_CHANNEL=stack
|
|||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
RECAPTCHA_SITE_KEY=ClientKeyHere
|
||||
RECAPTCHA_SECRET_KEY=ServerKeyHere
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Session;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DoubleSessionBlockController extends Controller
|
||||
{
|
||||
public function create()
|
||||
{
|
||||
return response()
|
||||
->view('auth.ddos_blocked', [], 403);
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
request()->validate([
|
||||
'g-recaptcha-response' => [new \App\Rules\GoogleRecaptcha]
|
||||
]);
|
||||
|
||||
$record = Session::where('id', session()->getId())->first();
|
||||
if($record) {
|
||||
$record->bypass_block_screen = true;
|
||||
$record->save();
|
||||
|
||||
$returnUrl = request()->input('ReturnUrl');
|
||||
|
||||
if(!$returnUrl)
|
||||
$returnUrl = '/';
|
||||
|
||||
return redirect(urldecode($returnUrl), 302);
|
||||
} else {
|
||||
return redirect()->back()->withErrors('Could not unblock. Try again.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ class Kernel extends HttpKernel
|
|||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\DoubleSessionProtector::class, // Prevents DDoS attacks.
|
||||
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
|
|
@ -37,6 +39,7 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
|
||||
\App\Http\Middleware\DoubleSessionProtector::class, // Prevents DDoS attacks.
|
||||
\App\Http\Middleware\DailyReward::class
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Session;
|
||||
|
||||
class DoubleSessionProtector
|
||||
{
|
||||
protected function handlePage(Request $request, Closure $next) {
|
||||
if($request->route()->getName() != 'ddos.bypass' && !$request->isMethod('post')) {
|
||||
return redirect()
|
||||
->to(route('ddos.bypass', ['ReturnUrl' => urlencode('/'.$request->path())]), 302);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$record = Session::where('id', session()->getId())->where('bypass_block_screen', true)->first();
|
||||
if($record) {
|
||||
if($request->route()->getName() == 'ddos.bypass') {
|
||||
return redirect('/', 302);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
$record = Session::where('ip_address', $request->ip());
|
||||
if($record->exists()) {
|
||||
foreach($record->get() as $session) {
|
||||
if($session->id != session()->getId())
|
||||
return $this->handlePage($request, $next);
|
||||
}
|
||||
}
|
||||
|
||||
if($request->route()->getName() == 'ddos.bypass') {
|
||||
$returnUrl = $request->input('ReturnUrl');
|
||||
|
||||
if(!$returnUrl)
|
||||
$returnUrl = '/';
|
||||
|
||||
return redirect('/', 302);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Session extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'id',
|
||||
'ip_address',
|
||||
'payload',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => 'string',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class GoogleRecaptcha implements Rule
|
||||
{
|
||||
/**
|
||||
* Create a new rule instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'g-recaptcha-response.required' => 'Please solve the captcha.'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
$client = new Client();
|
||||
$response = $client->post('https://www.google.com/recaptcha/api/siteverify',
|
||||
[
|
||||
'form_params' => [
|
||||
'secret' => env('RECAPTCHA_SECRET_KEY'),
|
||||
'remoteip' => request()->ip(),
|
||||
'response' => $value
|
||||
]
|
||||
]
|
||||
);
|
||||
$body = json_decode((string)$response->getBody());
|
||||
return $body->success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return 'Please solve the captcha.';
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'file'),
|
||||
'driver' => 'database',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->text('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
$table->boolean('bypass_block_screen')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
@php
|
||||
$noFooter = true;
|
||||
$noNav = true;
|
||||
@endphp
|
||||
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Captcha')
|
||||
|
||||
@section('extra-headers')
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="container m-auto">
|
||||
<x-card>
|
||||
<x-slot name="title">
|
||||
BOOP BOOP. BEEP?
|
||||
</x-slot>
|
||||
<x-slot name="body">
|
||||
<div class="p-2 mb-2 d-flex flex-column justify-content-center">
|
||||
<h5>Are you a ROBOT?</h5>
|
||||
<p>We've detected unusual activity coming from your network. To protect ourselves from DDoS attacks and other forms of web abuse, we've blocked your request. Solve the captcha below to regain access to the website.</p>
|
||||
|
||||
<div class="mt-3">
|
||||
@if ($errors->any())
|
||||
<div class="px-3 mb-10">
|
||||
<div class="alert alert-danger graphictoria-alert graphictoria-error-popup">{{ $errors->first() }}</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('ddos.bypass') }}">
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="ReturnUrl" value="{{ request()->input('ReturnUrl') }}" />
|
||||
|
||||
<div class="d-flex">
|
||||
<div class="g-recaptcha mb-2 mx-auto" data-sitekey="{{ env('RECAPTCHA_SITE_KEY') }}"></div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary px-5" type="submit">Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
<x-slot name="footer">
|
||||
<p class="text-muted">If this page continues to show up after you solve the captcha, please contact us at contact us at <a href="mailto:support@gtoria.net" class="fw-bold text-decoration-none">support@gtoria.net</a> and we'll be happy to help.</p>
|
||||
</x-slot>
|
||||
</x-card>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use App\Http\Controllers\Auth\AuthenticatedSessionController;
|
||||
use App\Http\Controllers\Auth\ConfirmablePasswordController;
|
||||
use App\Http\Controllers\Auth\DoubleSessionBlockController;
|
||||
use App\Http\Controllers\Auth\EmailVerificationNotificationController;
|
||||
use App\Http\Controllers\Auth\EmailVerificationPromptController;
|
||||
use App\Http\Controllers\Auth\NewPasswordController;
|
||||
|
|
@ -30,6 +31,10 @@ Route::get('/my/dashboard', function () {
|
|||
return view('dashboard');
|
||||
})->middleware(['auth'])->name('dashboard');
|
||||
|
||||
Route::get('request-blocked', [DoubleSessionBlockController::class, 'create'])
|
||||
->name('ddos.bypass');
|
||||
Route::post('request-blocked', [DoubleSessionBlockController::class, 'store']);
|
||||
|
||||
Route::middleware('guest')->group(function () {
|
||||
Route::get('register', [RegisteredUserController::class, 'create'])
|
||||
->name('register');
|
||||
|
|
|
|||
Loading…
Reference in New Issue