diff --git a/app/Http/Controllers/HumanVerification.php b/app/Http/Controllers/HumanVerification.php index e3de31f753a9d312ecce18aa469c07801426aa56..def5420560a44971a1a5d12db0a0ab9f58ff465a 100644 --- a/app/Http/Controllers/HumanVerification.php +++ b/app/Http/Controllers/HumanVerification.php @@ -20,23 +20,25 @@ 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:'); + ->with('errorMessage', 'Fehler: Falsches Captcha eingegeben!'); } else { # 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]); + # The Captcha was correct. We can remove the key from the user + DB::table('humanverification')->where('uid', $id)->update(['locked' => false, 'lockedKey' => "", 'whitelist' => 1]); return redirect($url); } else { return redirect('/'); @@ -44,8 +46,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 +58,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 +73,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)->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 5a37786be0a1b206f4d624053260c30e5a6a3156..0be2058000507fb79acf245d34a706eeb057117e 100644 --- a/app/Http/Middleware/HumanVerification.php +++ b/app/Http/Middleware/HumanVerification.php @@ -7,6 +7,7 @@ use Carbon; use Closure; use DB; use Illuminate\Http\Response; +use URL; class HumanVerification { @@ -20,7 +21,9 @@ class HumanVerification public function handle($request, Closure $next) { try { - $id = md5($request->ip()); + $id = hash("sha512", $request->ip()); + $uid = hash("sha512", $request->ip() . $_SERVER["AGENT"]); + unset($_SERVER["AGENT"]); /** * If the user sends a Password or a key @@ -28,57 +31,102 @@ class HumanVerification * 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)) { + if ($request->filled('password') || $request->filled('key') || $request->filled('appversion') || !env('BOT_PROTECTION', false)) { return $next($request); } - $user = DB::table('humanverification')->where('id', $id)->first(); + // 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( - ['id' => $id, 'unusedResultPages' => 1, 'locked' => false, "lockedKey" => "", 'updated_at' => Carbon::now()] + [ + '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(['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('usedurls')->insert(['uid' => $uid, 'id' => $id, 'eingabe' => $request->input('eingabe', '')]); + $user = DB::table('humanverification')->where('uid', $uid)->first(); + } + + # Lock out everyone in a Bot network + # Find out how many requests this IP has made + $sum = DB::table('humanverification')->where('id', $id)->where('whitelist', false)->sum('unusedResultPages'); + + # A lot of automated requests are from websites that redirect users to our result page. + # We will detect those requests and put a captcha + $referer = URL::previous(); + # Just the URL-Parameter + $refererLock = false; + if (stripos($referer, "?") !== false) { + $referer = substr($referer, stripos($referer, "?") + 1); + $referer = urldecode($referer); + if (preg_match("/http[s]{0,1}:\/\/metager\.de\/meta\/meta.ger3\?.*?eingabe=([\w\d]+\.){1,2}[\w\d]+/si", $referer) === 1) { + $refererLock = 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()]); + } - $request->request->add(['verification_id' => $id, 'verification_count' => $unusedResultPages]); + // Defines if this is the only user using that IP Adress + $alone = DB::table('humanverification')->where('id', $id)->count() === 1; + if ((!$alone && $sum >= 50 && $user->whitelist !== 1) || $refererLock) { + DB::table('humanverification')->where('uid', $uid)->update(['locked' => true]); + $user->locked = 1; + } # 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"]]); + DB::table('humanverification')->where('uid', $uid)->update(['lockedKey' => $captcha["key"]]); return new Response( - view('captcha') + view('humanverification.captcha') ->with('title', "Bestätigung erforderlich") - ->with('id', $id) + ->with('id', $uid) ->with('url', url()->full()) ->with('image', $captcha["img"]) ); } + + $unusedResultPages = intval($user->unusedResultPages); + $unusedResultPages++; + $locked = false; + + if ($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 + + if ($unusedResultPages === 50 || $unusedResultPages === 75 || $unusedResultPages === 85 || $unusedResultPages >= 90) { + $locked = true; + } + + } + 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', '')]); + } 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 0a03f4e4ffc395ee9b063177ccd81d5b7f99722f..57d89a841fd1ba1608578ce416f69c13ec96ba1e 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 95c94233c98e95c00a0a6381c478362e80bd9ff4..081612b3be8e193afa413d869ba037879e06311b 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 341d0b664072a7095838b0adb61e769a084b8c91..a7cc0f956d785f8aec45f91641c7fd8ac26c0ccf 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 b67e0a6e9b0ce993d445cd7ffb898622f8f8d4c3..4334ce537cd2b6c2d2428f9ca992cb4ac220db04 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/captcha.blade.php deleted file mode 100644 index 250332fb52b521588324fca4068b1a206abd5898..0000000000000000000000000000000000000000 --- a/resources/views/captcha.blade.php +++ /dev/null @@ -1,22 +0,0 @@ -@extends('layouts.subPages') - -@section('title', $title ) - -@section('content') - <h1>Entschuldigen Sie die Störung</h1> - <p>Wir haben Grund zur Annahme, dass von Ihrem Anschluss verstärkt automatisierte Abfragen abgeschickt wurden. - Deshalb bitten wir Sie, die nachfolgende Captcha Abfrage zu beantworten.</p> - <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> - <form method="post" action="{{ route('verification', ['id' => $id]) }}"> - <input type="hidden" name="url" value="{!! $url !!}"> - <input type="hidden" name="id" value="{{ $id }}"> - <p><img src="{{ $image }}" /></p> - @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> - </form> -@endsection diff --git a/resources/views/humanverification/captcha.blade.php b/resources/views/humanverification/captcha.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..9f58d36e3122ab75bda226e399157b23fc58f110 --- /dev/null +++ b/resources/views/humanverification/captcha.blade.php @@ -0,0 +1,23 @@ +@extends('layouts.subPages') + +@section('title', $title ) + +@section('content') + <h1>Entschuldigen Sie die Störung</h1> + <p>Sie befinden sich in einem Netzwerk aus dem wir verstärkt automatisierte Anfragen erhalten. Keine Sorge: Das bedeutet nicht unbedingt, dass diese Anfragen von Ihrem PC kommen.</p> + <p>Allerdings können wir Ihre Anfragen nicht von denen des "Robots" unterscheiden. Zum Schutz der von uns abgefragten Suchmaschinen müssen wir aber sicherstellen, dass diese nicht mit (automatisierten) Abfragen überflutet werden.</p> + + <p>Bitte geben Sie deshalb die Zeichen 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 }}"> + <p><img src="{{ $image }}" /></p> + @if(isset($errorMessage)) + <p><font color="red">{{$errorMessage}}</font></p> + @endif + <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> + <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 }} +@endsection diff --git a/resources/views/layouts/resultPage.blade.php b/resources/views/layouts/resultPage.blade.php index 6d3b6ab12eff9b843047fc6639fdca0af799e9b8..a8c3853481d645c18412363f11b57d2efb0ea8d1 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 7e3b997720a33862391f5bb4ea04abfd65cb3a80..f1ebb289a2c7cfd5ed37465d92e57eb96ac12300 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');