From d39efe60c537f1638258a8350285fa07614300f6 Mon Sep 17 00:00:00 2001 From: Dominik Pfennig <dominik@suma-ev.de> Date: Fri, 14 Sep 2018 12:46:38 +0200 Subject: [PATCH] New Humanverification version --- app/Http/Controllers/HumanVerification.php | 60 +++++-- app/Http/Middleware/HumanVerification.php | 148 ++++++++++++------ app/MetaGer.php | 12 +- ..._114745_create_humanverification_table.php | 10 +- .../migrations/2018_05_03_101301_usedurls.php | 13 +- public/index.php | 2 + .../{ => humanverification}/captcha.blade.php | 5 +- .../whitelistVerification.blade.php | 25 +++ resources/views/layouts/resultPage.blade.php | 4 +- routes/web.php | 1 + 10 files changed, 205 insertions(+), 75 deletions(-) rename resources/views/{ => humanverification}/captcha.blade.php (75%) create mode 100644 resources/views/humanverification/whitelistVerification.blade.php diff --git a/app/Http/Controllers/HumanVerification.php b/app/Http/Controllers/HumanVerification.php index e3de31f75..147c9f13c 100644 --- a/app/Http/Controllers/HumanVerification.php +++ b/app/Http/Controllers/HumanVerification.php @@ -20,23 +20,28 @@ class HumanVerification extends Controller } if ($request->getMethod() == 'POST') { - $user = DB::table('humanverification')->where('id', $id)->first(); + $user = DB::table('humanverification')->where('uid', $id)->first(); $lockedKey = $user->lockedKey; $key = $request->input('captcha'); $key = strtolower($key); + if (!$hasher->check($key, $lockedKey)) { $captcha = Captcha::create("default", true); - DB::table('humanverification')->where('id', $id)->update(['lockedKey' => $captcha["key"]]); - return view('captcha')->with('title', 'Bestätigung notwendig') + DB::table('humanverification')->where('uid', $id)->update(['lockedKey' => $captcha["key"]]); + return view('humanverification.captcha')->with('title', 'Bestätigung notwendig') ->with('id', $id) ->with('url', $url) ->with('image', $captcha["img"]) ->with('errorMessage', 'Bitte Captcha eingeben:'); } else { + + # The Captcha was correct. We can remove the key from the user + DB::table('humanverification')->where('uid', $id)->update(['lockedKey' => "", 'whitelistCounter' => 0]); + # If we can unlock the Account of this user we will redirect him to the result page if ($user !== null && $user->locked === 1) { - DB::table('humanverification')->where('id', $id)->update(['locked' => false]); + DB::table('humanverification')->where('uid', $id)->update(['locked' => false]); return redirect($url); } else { return redirect('/'); @@ -44,8 +49,8 @@ class HumanVerification extends Controller } } $captcha = Captcha::create("default", true); - DB::table('humanverification')->where('id', $id)->update(['lockedKey' => $captcha["key"]]); - return view('captcha')->with('title', 'Bestätigung notwendig') + DB::table('humanverification')->where('uid', $id)->update(['lockedKey' => $captcha["key"]]); + return view('humanverification.captcha')->with('title', 'Bestätigung notwendig') ->with('id', $id) ->with('url', $url) ->with('image', $captcha["img"]); @@ -56,10 +61,9 @@ class HumanVerification extends Controller if (!$request->has('mm')) { abort(404, "Keine Katze gefunden."); } - $id = md5($request->ip()); + if (HumanVerification::checkId($request, $request->input('mm'))) { - # Remove the entry from the database - DB::table('humanverification')->where('id', $id)->where('updated_at', '<', Carbon::NOW()->subSeconds(2))->delete(); + HumanVerification::removeUser($request, $request->input('mm')); } return response(hex2bin('89504e470d0a1a0a0000000d494844520000000100000001010300000025db56ca00000003504c5445000000a77a3dda0000000174524e530040e6d8660000000a4944415408d76360000000020001e221bc330000000049454e44ae426082'), 200) ->header('Content-Type', 'image/png'); @@ -72,15 +76,47 @@ class HumanVerification extends Controller # If the user is correct and the password is we will delete any entry in the database $requiredPass = md5($mm . Carbon::NOW()->day . $url . env("PROXY_PASSWORD")); if (HumanVerification::checkId($request, $mm) && $requiredPass === $password) { - # Remove the entry from the database - DB::table('humanverification')->where('id', $mm)->where('updated_at', '<', Carbon::NOW()->subSeconds(2))->delete(); + HumanVerification::removeUser($request, $mm); } return redirect($url); } + private static function removeUser($request, $uid) + { + $id = hash("sha512", $request->ip()); + + $sum = DB::table('humanverification')->where('id', $id)->where('whitelist', false)->sum('unusedResultPages'); + $user = DB::table('humanverification')->where('uid', $uid)->first(); + + if ($user === null) { + return; + } + + # Check if we have to whitelist the user or if we can simply delete the data + if ($user->unusedResultPages < $sum && $user->whitelist === 0) { + # Whitelist + DB::table('humanverification')->where('uid', $uid)->update(['whitelist' => true, 'whitelistCounter' => 0]); + $user->whitelist = 1; + $user->whitelistCounter = 0; + } + + if ($user->whitelist === 1) { + if ( + DB::table('humanverification')->where('uid', $uid)->where('updated_at', '<', Carbon::NOW()->subSeconds(2))->update(['unusedResultPages' => 0]) + === 1 + ) { + DB::table('usedurls')->where('uid', $uid)->delete(); + } + } else { + DB::table('humanverification')->where('uid', $uid)->where('updated_at', '<', Carbon::NOW()->subSeconds(2))->delete(); + + } + + } + private static function checkId($request, $id) { - if (md5($request->ip()) === $id) { + if (hash("sha512", $request->ip() . $_SERVER["AGENT"]) === $id) { return true; } else { return false; diff --git a/app/Http/Middleware/HumanVerification.php b/app/Http/Middleware/HumanVerification.php index 5a37786be..d171390d3 100644 --- a/app/Http/Middleware/HumanVerification.php +++ b/app/Http/Middleware/HumanVerification.php @@ -19,66 +19,116 @@ class HumanVerification */ public function handle($request, Closure $next) { - try { - $id = md5($request->ip()); - - /** - * If the user sends a Password or a key - * We will not verificate the user. - * If someone that uses a bot finds this out we - * might have to change it at some point. - */ - if ($request->has('password') || $request->filled('key') || $request->filled('appversion') || !env('BOT_PROTECTION', false)) { - return $next($request); - } + //try { + $id = hash("sha512", $request->ip()); + $uid = hash("sha512", $request->ip() . $_SERVER["AGENT"]); + unset($_SERVER["AGENT"]); + + /** + * If the user sends a Password or a key + * We will not verificate the user. + * If someone that uses a bot finds this out we + * might have to change it at some point. + */ + if ($request->filled('password') || $request->filled('key') || $request->filled('appversion') || !env('BOT_PROTECTION', false)) { + return $next($request); + } + + // The specific user + $user = DB::table('humanverification')->where('uid', $uid)->first(); + + $createdAt = Carbon::now(); + $unusedResultPages = 1; + $locked = false; + # If this user doesn't have an entry we will create one + + if ($user === null) { + DB::table('humanverification')->insert( + [ + 'uid' => $uid, + 'id' => $id, + 'unusedResultPages' => 0, + 'whitelist' => false, + 'whitelistCounter' => 0, + 'locked' => false, + "lockedKey" => "", + 'updated_at' => Carbon::now(), + ] + ); + # Insert the URL the user tries to reach + $url = url()->full(); + DB::table('usedurls')->insert(['uid' => $uid, 'id' => $id, 'eingabe' => $request->input('eingabe', '')]); + $user = DB::table('humanverification')->where('uid', $uid)->first(); + } + + # If the user is locked we will force a Captcha validation + if ($user->locked === 1) { + $captcha = Captcha::create("default", true); + DB::table('humanverification')->where('uid', $uid)->update(['lockedKey' => $captcha["key"]]); + return + new Response( + view('humanverification.captcha') + ->with('title', "Bestätigung erforderlich") + ->with('id', $uid) + ->with('url', url()->full()) + ->with('image', $captcha["img"]) + ); + } + + # Find out how many requests this IP has made + $sum = DB::table('humanverification')->where('id', $id)->where('whitelist', false)->sum('unusedResultPages'); - $user = DB::table('humanverification')->where('id', $id)->first(); - $createdAt = Carbon::now(); - $unusedResultPages = 1; + // Defines if this is the only user using that IP Adress + $alone = DB::table('humanverification')->where('id', $id)->count() === 1; + + $unusedResultPages = intval($user->unusedResultPages); + $unusedResultPages++; + + if ($sum < 50 || $alone || $user->whitelist === 1) { + # This IP doesn't need verification yet + # The user currently isn't locked + + # We have different security gates: + # 50, 75, 85, >=90 => Captcha validated Result Pages + # If the user shows activity on our result page the counter will be deleted + # Maybe I'll add a ban if the user reaches 100 $locked = false; - # If this user doesn't have an entry we will create one - if ($user === null) { - DB::table('humanverification')->insert( - ['id' => $id, 'unusedResultPages' => 1, 'locked' => false, "lockedKey" => "", 'updated_at' => Carbon::now()] - ); - # Insert the URL the user tries to reach - $url = url()->full(); - DB::table('usedurls')->insert(['user_id' => $id, 'url' => $url]); - $user = DB::table('humanverification')->where('id', $id)->first(); - } else if ($user->locked !== 1) { - $unusedResultPages = intval($user->unusedResultPages); - $unusedResultPages++; - # We have different security gates: - # 50, 75, 85, >=90 => Captcha validated Result Pages - # If the user shows activity on our result page the counter will be deleted - # Maybe I'll add a ban if the user reaches 100 - if ($unusedResultPages === 50 || $unusedResultPages === 75 || $unusedResultPages === 85 || $unusedResultPages >= 90) { - $locked = true; - } - DB::table('humanverification')->where('id', $id)->update(['unusedResultPages' => $unusedResultPages, 'locked' => $locked, 'updated_at' => $createdAt]); - # Insert the URL the user tries to reach - DB::table('usedurls')->insert(['user_id' => $id, 'url' => url()->full()]); + if ($unusedResultPages === 50 || $unusedResultPages === 75 || $unusedResultPages === 85 || $unusedResultPages >= 90) { + $locked = true; } - $request->request->add(['verification_id' => $id, 'verification_count' => $unusedResultPages]); + DB::table('humanverification')->where('uid', $uid)->update(['unusedResultPages' => $unusedResultPages, 'locked' => $locked]); + # Insert the URL the user tries to reach + DB::table('usedurls')->insert(['uid' => $uid, 'id' => $id, 'eingabe' => $request->input('eingabe', '')]); + } else { + $tmpId = md5($uid . date("d")); - # If the user is locked we will force a Captcha validation - if ($user->locked === 1) { - $captcha = Captcha::create("default", true); - DB::table('humanverification')->where('id', $id)->update(['lockedKey' => $captcha["key"]]); + # If the parameter uid is correctly set we will allow access to the result page + if ($request->input('uid', '') !== $tmpId) { + DB::table('humanverification')->where('uid', $uid)->increment('whitelistCounter'); + if ($user->whitelistCounter >= 4) { + DB::table('humanverification')->where('uid', $uid)->update(['locked' => true]); + } + + # This IP will need verification return new Response( - view('captcha') - ->with('title', "Bestätigung erforderlich") - ->with('id', $id) - ->with('url', url()->full()) - ->with('image', $captcha["img"]) + view('humanverification.whitelistVerification') + ->with('title', $request->input('eingabe', '') . " - MetaGer") + ->with('method', $request->method()) + ->with('uid', md5($uid . date("d"))) ); + } else { + DB::table('humanverification')->where('uid', $uid)->update(['unusedResultPages' => $unusedResultPages]); + # Insert the URL the user tries to reach + DB::table('usedurls')->insert(['uid' => $uid, 'id' => $id, 'eingabe' => $request->input('eingabe', '')]); } - } catch (\Illuminate\Database\QueryException $e) { - // Failure in contacting metager3.de } + /* } catch (\Illuminate\Database\QueryException $e) { + // Failure in contacting metager3.de + }*/ + $request->request->add(['verification_id' => $uid, 'verification_count' => $unusedResultPages]); return $next($request); } } diff --git a/app/MetaGer.php b/app/MetaGer.php index 0a03f4e4f..57d89a841 100644 --- a/app/MetaGer.php +++ b/app/MetaGer.php @@ -934,7 +934,7 @@ class MetaGer public function parseFormData(Request $request) { - $this->request = $request; + # Sichert, dass der request in UTF-8 formatiert ist if ($request->input('encoding', 'utf8') !== "utf8") { # In früheren Versionen, als es den Encoding Parameter noch nicht gab, wurden die Daten in ISO-8859-1 übertragen @@ -1051,6 +1051,8 @@ class MetaGer $this->verificationId = $request->input('verification_id', null); $this->verificationCount = intval($request->input('verification_count', '0')); $this->apiKey = $request->input('key', ''); + // Remove Inputs that are not used + $this->request = $request->replace($request->except(['verification_id', 'uid', 'verification_count'])); $this->validated = false; if (isset($this->password)) { @@ -1492,6 +1494,14 @@ class MetaGer # Einfache Getter + public function getVerificationId() { + return $this->verificationId; + } + + public function getVerificationCount() { + return $this->verificationCount; + } + public function getSite() { return $this->site; diff --git a/database/migrations/2018_04_26_114745_create_humanverification_table.php b/database/migrations/2018_04_26_114745_create_humanverification_table.php index 95c94233c..081612b3b 100644 --- a/database/migrations/2018_04_26_114745_create_humanverification_table.php +++ b/database/migrations/2018_04_26_114745_create_humanverification_table.php @@ -1,8 +1,8 @@ <?php -use Illuminate\Support\Facades\Schema; -use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; class CreateHumanverificationTable extends Migration { @@ -14,9 +14,13 @@ class CreateHumanverificationTable extends Migration public function up() { Schema::create('humanverification', function (Blueprint $table) { - $table->string('id')->unique(); + $table->string('uid')->unique(); + $table->string('id'); $table->integer('unusedResultPages'); + $table->boolean('whitelist'); + $table->integer('whitelistCounter'); $table->boolean('locked'); + $table->string('lockedKey'); $table->timestamp('updated_at'); }); } diff --git a/database/migrations/2018_05_03_101301_usedurls.php b/database/migrations/2018_05_03_101301_usedurls.php index 341d0b664..a7cc0f956 100644 --- a/database/migrations/2018_05_03_101301_usedurls.php +++ b/database/migrations/2018_05_03_101301_usedurls.php @@ -1,8 +1,8 @@ <?php -use Illuminate\Support\Facades\Schema; -use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; class Usedurls extends Migration { @@ -14,11 +14,12 @@ class Usedurls extends Migration public function up() { Schema::create('usedurls', function (Blueprint $table) { - $table->increments('id')->unique(); - $table->string('user_id'); - $table->text('url'); + $table->increments('number')->unique(); + $table->string('uid'); + $table->string('id'); + $table->text('eingabe'); $table->timestamp('created_at'); - $table->foreign('user_id')->references('id')->on('humanverification')->onDelete('cascade'); + $table->foreign('uid')->references('uid')->on('humanverification')->onDelete('cascade'); }); } diff --git a/public/index.php b/public/index.php index b67e0a6e9..4334ce537 100644 --- a/public/index.php +++ b/public/index.php @@ -13,6 +13,8 @@ if (isset($_SERVER["HTTP_FORWARDED"]) && isset($_SERVER["HTTP_X_FORWARDED_FOR"]) unset($_SERVER["HTTP_FORWARDED"]); } +$_SERVER["AGENT"] = $_SERVER["HTTP_USER_AGENT"]; + if (isset($_SERVER['HTTP_USER_AGENT'])) { $agentPieces = explode(" ", $_SERVER['HTTP_USER_AGENT']); diff --git a/resources/views/captcha.blade.php b/resources/views/humanverification/captcha.blade.php similarity index 75% rename from resources/views/captcha.blade.php rename to resources/views/humanverification/captcha.blade.php index 250332fb5..6423a8816 100644 --- a/resources/views/captcha.blade.php +++ b/resources/views/humanverification/captcha.blade.php @@ -9,6 +9,7 @@ <p>Sollten Sie diese Nachricht häufiger sehen oder handelt es sich dabei um einen Irrtum, schicken Sie uns gerne eine Nachricht über unser <a href="/kontakt">Kontaktformular</a>.</p> <p>Nennen Sie uns in diesem Fall bitte unbedingt folgende Vorgangsnummer: {{ $id }} <p>Wir schauen uns den Vorgang dann gerne im Detail an.</p> + <p>Bitte geben SIe die Buchstaben aus dem Bild in die Eingabebox ein und bestätigen Sie mit OK um zur Ergebnisseite zu gelangen.</p> <form method="post" action="{{ route('verification', ['id' => $id]) }}"> <input type="hidden" name="url" value="{!! $url !!}"> <input type="hidden" name="id" value="{{ $id }}"> @@ -16,7 +17,7 @@ @if(isset($errorMessage)) <p><font color="red">{{$errorMessage}}</font></p> @endif - <p><input type="text" name="captcha"></p> - <p><button type="submit" name="check">OK</button></p> + <p><input type="text" class="form-control" name="captcha" placeholder="Captcha eingeben" autofocus></p> + <p><button type="submit" class="btn btn-success" name="check">OK</button></p> </form> @endsection diff --git a/resources/views/humanverification/whitelistVerification.blade.php b/resources/views/humanverification/whitelistVerification.blade.php new file mode 100644 index 000000000..23a7e4aa0 --- /dev/null +++ b/resources/views/humanverification/whitelistVerification.blade.php @@ -0,0 +1,25 @@ +@extends('layouts.subPages') + +@section('title', $title ) + +@section('content') + <h1 class="page-title">Einen kurzen Augenblick bitte</h1> + <div class="card-heavy"> + <p>Sie befinden sich im selben Netzwerk, aus dem wir sehr viele automatisierte Anfragen erhalten. Das bedeutet nicht, dass die Anfragen von Ihrem PC kommen. Wir müssen nun aber verifizieren, dass es sich bei Ihnen um einen realen Nutzer handelt.</p> + <p>Der erste Schritt ist diese Vorschaltseite. Sie brauchen nichts weiter tun, als unten auf den Knopf "Weiter zur Ergebnisseite" zu klicken.</p> + <p>Zukünftig sollten Sie diese Seite nicht mehr sehen.</p> + <form id="goOn" method="{{ $method }}"> + @foreach(Request::all() as $key => $value) + <input type="hidden" name="{{ $key }}" value="{{ $value }}" /> + @endforeach + <input type="hidden" name="uid" value="{{ $uid }}" /> + <p><button type="submit" class="btn btn-success">Weiter zur Ergebnisseite</button></p> + </form> + </div> + <script> + $(document).ready(function() { + $(".mg-panel").css("display", "none"); + $("#goOn").submit(); + }); + </script> +@endsection diff --git a/resources/views/layouts/resultPage.blade.php b/resources/views/layouts/resultPage.blade.php index 6d3b6ab12..a8c385348 100644 --- a/resources/views/layouts/resultPage.blade.php +++ b/resources/views/layouts/resultPage.blade.php @@ -9,8 +9,8 @@ <meta name="p" content="{{ getmypid() }}" /> <meta name="q" content="{{ $eingabe }}" /> <meta name="l" content="{{ LaravelLocalization::getCurrentLocale() }}" /> - <meta name="mm" content="{{ Request::input('verification_id') }}" /> - <meta name="mn" content="{{ Request::input('verification_count') }}" /> + <meta name="mm" content="{{ $metager->getVerificationId() }}" /> + <meta name="mn" content="{{ $metager->getVerificationCount() }}" /> <link rel="search" type="application/opensearchdescription+xml" title="{!! trans('resultPage.opensearch') !!}" href="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), action('StartpageController@loadPlugin', ['params' => base64_encode(serialize(Request::all()))])) }}"> <link type="text/css" rel="stylesheet" href="{{ mix('css/themes/default.css') }}" /> <link type="text/css" rel="stylesheet" href="/font-awesome/css/font-awesome.min.css" /> diff --git a/routes/web.php b/routes/web.php index 7e3b99772..f1ebb289a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -164,6 +164,7 @@ Route::group( Route::match(['get', 'post'], 'meta/meta.ger3', 'MetaGerSearch@search')->middleware('humanverification'); Route::post('img/cat.jpg', 'HumanVerification@remove'); Route::get('r/metager/{mm}/{pw}/{url}', ['as' => 'humanverification', 'uses' => 'HumanVerification@removeGet']); + Route::post('img/dog.jpg', 'HumanVerification@whitelist'); Route::get('meta/picture', 'Pictureproxy@get'); Route::get('clickstats', 'LogController@clicklog'); -- GitLab