From cec37aceb6e131fb12f9536597bef1d665d90c9b Mon Sep 17 00:00:00 2001 From: Dominik Hebeler <dominik@suma-ev.de> Date: Mon, 4 Mar 2019 10:39:26 +0100 Subject: [PATCH] Changed Human Verification to use Redis --- app/Http/Controllers/HumanVerification.php | 32 +++++++++++++++++----- app/Http/Middleware/HumanVerification.php | 29 ++++++++++++-------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/HumanVerification.php b/app/Http/Controllers/HumanVerification.php index d236ecf29..4bf67f6a7 100644 --- a/app/Http/Controllers/HumanVerification.php +++ b/app/Http/Controllers/HumanVerification.php @@ -12,9 +12,12 @@ use Input; class HumanVerification extends Controller { const PREFIX = "humanverification"; + const EXPIRELONG = 60 * 60 * 24 * 14; + const EXPIRESHORT = 60 * 60 * 72; public static function captcha(Request $request, Hasher $hasher, $id, $url = null) { + $redis = Redis::connection('REDIS_CACHE_HOST'); if ($url != null) { $url = base64_decode(str_replace("<<SLASH>>", "/", $url)); @@ -24,7 +27,7 @@ class HumanVerification extends Controller if ($request->getMethod() == 'POST') { - $user = Redis::hgetall(HumanVerification::PREFIX . "." . $id); + $user = $redis->hgetall(HumanVerification::PREFIX . "." . $id); $user = ['uid' => $user["uid"], 'id' => $user["id"], 'unusedResultPages' => intval($user["unusedResultPages"]), @@ -39,7 +42,10 @@ class HumanVerification extends Controller if (!$hasher->check($key, $lockedKey)) { $captcha = Captcha::create("default", true); - Redis::hset(HumanVerification::PREFIX . "." . $id, 'lockedKey', $captcha["key"]); + $pipeline = $redis->pipeline(); + $pipeline->hset(HumanVerification::PREFIX . "." . $id, 'lockedKey', $captcha["key"]); + $pipeline->expire(HumanVerification::PREFIX . "." . $id, $user["whitelist"] ? HumanVerification::EXPIRELONG : HumanVerification::EXPIRESHORT); + $pipeline->execute(); return view('humanverification.captcha')->with('title', 'Bestätigung notwendig') ->with('id', $id) ->with('url', $url) @@ -49,7 +55,13 @@ class HumanVerification extends Controller # If we can unlock the Account of this user we will redirect him to the result page if ($user !== null && $user["locked"]) { # The Captcha was correct. We can remove the key from the user - Redis::hmset(HumanVerification::PREFIX . "." . $id, ['locked' => "0", 'lockedKey' => ""]); + # If the sum of all users with that ip is too high we need to whitelist the user or they will receive a captcha again on the next request + $sum = 0; + $users = []; + $pipeline = $redis->pipeline(); + $pipeline->hmset(HumanVerification::PREFIX . "." . $id, ['locked' => "0", 'lockedKey' => "", 'whitelist' => '1']); + $pipeline->expire(HumanVerification::PREFIX . "." . $id, $user["whitelist"] ? HumanVerification::EXPIRELONG : HumanVerification::EXPIRESHORT); + $pipeline->execute(); return redirect($url); } else { return redirect('/'); @@ -57,7 +69,10 @@ class HumanVerification extends Controller } } $captcha = Captcha::create("default", true); - Redis::hset(HumanVerification::PREFIX . "." . $id, 'lockedKey', $captcha["key"]); + $pipeline = $redis->pipeline(); + $pipeline->hset(HumanVerification::PREFIX . "." . $id, 'lockedKey', $captcha["key"]); + $pipeline->expire(HumanVerification::PREFIX . "." . $id, $user["whitelist"] ? HumanVerification::EXPIRELONG : HumanVerification::EXPIRESHORT); + $pipeline->execute(); return view('humanverification.captcha')->with('title', 'Bestätigung notwendig') ->with('id', $id) ->with('url', $url) @@ -92,10 +107,11 @@ class HumanVerification extends Controller private static function removeUser($request, $uid) { + $redis = Redis::conection('REDIS_CACHE_HOST'); $id = hash("sha512", $request->ip()); - $userList = Redis::smembers(HumanVerification::PREFIX . "." . $id); - $pipe = Redis::pipeline(); + $userList = $redis->smembers(HumanVerification::PREFIX . "." . $id); + $pipe = $redis->pipeline(); foreach ($userList as $userid) { $pipe->hgetall(HumanVerification::PREFIX . "." . $userid); } @@ -131,7 +147,7 @@ class HumanVerification extends Controller return; } - $pipeline = Redis::pipeline(); + $pipeline = $redis->pipeline(); # Check if we have to whitelist the user or if we can simply delete the data if ($user["unusedResultPages"] < $sum && !$user["whitelist"]) { # Whitelist @@ -145,6 +161,8 @@ class HumanVerification extends Controller $pipeline->hdel(HumanVerification::PREFIX . "." . $uid); $pipeline->srem(HumanVerification::PREFIX . "." . $id, $uid); } + $pipeline->expire(HumanVerification::PREFIX . "." . $uid, $user["whitelist"] ? HumanVerification::EXPIRELONG : HumanVerification::EXPIRESHORT); + $pipeline->expire(HumanVerification::PREFIX . "." . $id, HumanVerification::EXPIRELONG); $pipeline->execute(); } diff --git a/app/Http/Middleware/HumanVerification.php b/app/Http/Middleware/HumanVerification.php index 216136562..bb1247aff 100644 --- a/app/Http/Middleware/HumanVerification.php +++ b/app/Http/Middleware/HumanVerification.php @@ -4,6 +4,7 @@ namespace App\Http\Middleware; use Captcha; use Closure; +use Cookie; use Illuminate\Http\Response; use Illuminate\Support\Facades\Redis; use URL; @@ -23,6 +24,7 @@ class HumanVerification $user = null; $update = true; $prefix = "humanverification"; + $redis = Redis::connection('REDIS_CACHE_HOST'); try { $id = hash("sha512", $request->ip()); $uid = hash("sha512", $request->ip() . $_SERVER["AGENT"]); @@ -34,14 +36,14 @@ class HumanVerification * 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)) { + if ($request->filled('password') || $request->filled('key') || Cookie::get('key') !== null || $request->filled('appversion') || !env('BOT_PROTECTION', false)) { $update = false; return $next($request); } # Get all Users of this IP - $userList = Redis::smembers($prefix . "." . $id); - $pipe = Redis::pipeline(); + $userList = $redis->smembers($prefix . "." . $id); + $pipe = $redis->pipeline(); foreach ($userList as $userid) { $pipe->hgetall($prefix . "." . $userid); @@ -54,8 +56,10 @@ class HumanVerification # Lock out everyone in a Bot network # Find out how many requests this IP has made $sum = 0; - foreach ($usersData as $userTmp) { + foreach ($usersData as $index => $userTmp) { if (empty($userTmp)) { + // This is a key that has been expired and should be deleted + $redis->srem($prefix . "." . $id, $userList[$index]); continue; } $userNew = ['uid' => $userTmp["uid"], @@ -71,7 +75,7 @@ class HumanVerification } else { $users[] = $userNew; } - if ($userNew["whitelist"]) { + if (!$userNew["whitelist"]) { $sum += intval($userTmp["unusedResultPages"]); } @@ -150,22 +154,23 @@ class HumanVerification if ($update) { // Update the user in the database - $pipeline = Redis::pipeline(); + $pipeline = $redis->pipeline(); $pipeline->hmset($prefix . "." . $user['uid'], $user); $pipeline->sadd($prefix . "." . $user["id"], $user["uid"]); - $expireDate = now(); - $expireDateLong = date_add($expireDate, date_interval_create_from_date_string('2 weeks'))->timestamp; - $expireDateShort = date_add($expireDate, date_interval_create_from_date_string('2 weeks'))->timestamp; + // Expire in two weeks + $expireLong = 60 * 60 * 24 * 14; + // Expire in 72h + $expireShort = 60 * 60 * 72; if ($user["whitelist"]) { - $pipeline->expireat($prefix . "." . $user['uid'], $expireDateLong); + $pipeline->expire($prefix . "." . $user['uid'], $expireLong); } else { - $pipeline->expireat($prefix . "." . $user['uid'], $expireDateShort); + $pipeline->expire($prefix . "." . $user['uid'], $expireShort); } - $pipeline->expireat($prefix . "." . $user["id"], $expireDateLong); + $pipeline->expire($prefix . "." . $user["id"], $expireLong); $pipeline->execute(); } -- GitLab