HumanVerification.php 11.7 KB
Newer Older
Dominik Hebeler's avatar
Dominik Hebeler committed
1 2 3 4
<?php

namespace App\Http\Controllers;

5 6
use Captcha;
use Carbon;
Dominik Hebeler's avatar
Dominik Hebeler committed
7
use Cookie;
8
use Illuminate\Hashing\BcryptHasher as Hasher;
Dominik Hebeler's avatar
Dominik Hebeler committed
9
use Illuminate\Http\Request;
10
use Illuminate\Support\Facades\Cache;
11
use Illuminate\Support\Facades\Redis;
Dominik Hebeler's avatar
Dominik Hebeler committed
12 13 14 15
use Input;

class HumanVerification extends Controller
{
16
    const PREFIX = "humanverification";
17 18
    const EXPIRELONG = 60 * 60 * 24 * 14;
    const EXPIRESHORT = 60 * 60 * 72;
19

20
    public static function captcha(Request $request, Hasher $hasher, $id, $uid, $url = null)
21 22 23 24
    {
        if ($url != null) {
            $url = base64_decode(str_replace("<<SLASH>>", "/", $url));
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
25 26 27 28 29 30 31 32 33 34 35
            $url = $request->input('url', url("/"));
        }

        $protocol = "http://";

        if ($request->secure()) {
            $protocol = "https://";
        }

        if (stripos($url, $protocol . $request->getHttpHost()) !== 0) {
            $url = url("/");
36
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
37

38 39 40 41 42 43 44 45 46
        $userlist = Cache::get(HumanVerification::PREFIX . "." . $id, []);
        $user = null;

        if (sizeof($userlist) === 0 || empty($userlist[$uid])) {
            return redirect('/');
        } else {
            $user = $userlist[$uid];
        }

47
        if ($request->getMethod() == 'POST') {
48
            \App\PrometheusExporter::CaptchaAnswered();
49
            $lockedKey = $user["lockedKey"];
50 51
            $key = $request->input('captcha');
            $key = strtolower($key);
52

53
            if (!$hasher->check($key, $lockedKey)) {
54
                $captcha = Captcha::create("default", true);
55 56
                $user["lockedKey"] = $captcha["key"];
                HumanVerification::saveUser($user);
57
                \App\PrometheusExporter::CaptchaShown();
58
                return view('humanverification.captcha')->with('title', 'Bestätigung notwendig')
59
                    ->with('uid', $user["uid"])
60 61 62
                    ->with('id', $id)
                    ->with('url', $url)
                    ->with('image', $captcha["img"])
Dominik Hebeler's avatar
Dominik Hebeler committed
63
                    ->with('errorMessage', 'Fehler: Falsche Eingabe!');
64
            } else {
65
                \App\PrometheusExporter::CaptchaCorrect();
66
                # If we can unlock the Account of this user we will redirect him to the result page
67
                if ($user !== null && $user["locked"]) {
Dominik Hebeler's avatar
Dominik Hebeler committed
68
                    # The Captcha was correct. We can remove the key from the user
69 70 71 72 73
                    # Additionally we will whitelist him so he is not counted towards botnetwork
                    $user["locked"] = false;
                    $user["lockedKey"] = "";
                    $user["whitelist"] = true;
                    HumanVerification::saveUser($user);
Dominik Hebeler's avatar
Dominik Hebeler committed
74
                    return redirect($url);
75
                } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
76 77 78 79
                    return redirect('/');
                }
            }
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
80

81
        $captcha = Captcha::create("default", true);
82 83
        $user["lockedKey"] = $captcha["key"];
        HumanVerification::saveUser($user);
84
        \App\PrometheusExporter::CaptchaShown();
85
        return view('humanverification.captcha')->with('title', 'Bestätigung notwendig')
86
            ->with('uid', $user["uid"])
87 88 89
            ->with('id', $id)
            ->with('url', $url)
            ->with('image', $captcha["img"]);
Dominik Hebeler's avatar
Dominik Hebeler committed
90

Dominik Hebeler's avatar
Dominik Hebeler committed
91 92
    }

93 94 95
    public static function remove(Request $request)
    {
        if (!$request->has('mm')) {
Dominik Hebeler's avatar
Dominik Hebeler committed
96 97
            abort(404, "Keine Katze gefunden.");
        }
98

99
        if (HumanVerification::checkId($request, $request->input('mm'))) {
100
            HumanVerification::removeUser($request, $request->input('mm'));
Dominik Hebeler's avatar
Dominik Hebeler committed
101 102 103 104 105
        }
        return response(hex2bin('89504e470d0a1a0a0000000d494844520000000100000001010300000025db56ca00000003504c5445000000a77a3dda0000000174524e530040e6d8660000000a4944415408d76360000000020001e221bc330000000049454e44ae426082'), 200)
            ->header('Content-Type', 'image/png');
    }

106 107 108
    public static function removeGet(Request $request, $mm, $password, $url)
    {
        $url = base64_decode(str_replace("<<SLASH>>", "/", $url));
Dominik Hebeler's avatar
Dominik Hebeler committed
109 110
        # 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"));
Dominik Hebeler's avatar
Dominik Hebeler committed
111

112
        if (HumanVerification::checkId($request, $mm) && $requiredPass === $password) {
113
            HumanVerification::removeUser($request, $mm);
Dominik Hebeler's avatar
Dominik Hebeler committed
114 115 116 117
        }
        return redirect($url);
    }

118 119 120
    private static function saveUser($user)
    {
        $userList = Cache::get(HumanVerification::PREFIX . "." . $user["id"], []);
Dominik Hebeler's avatar
Dominik Hebeler committed
121

122 123 124 125 126
        if ($user["whitelist"]) {
            $user["expiration"] = now()->addWeeks(2);
        } else {
            $user["expiration"] = now()->addHours(72);
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
127
        $userList[$user["uid"]] = $user;
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
        Cache::put(HumanVerification::PREFIX . "." . $user["id"], $userList, now()->addWeeks(2));
    }

    private static function deleteUser($user)
    {
        $userList = Cache::get(HumanVerification::PREFIX . "." . $user["id"], []);
        $newUserList = [];
        $changed = false;

        foreach ($userList as $uid => $userTmp) {
            if ($userTmp["uid"] !== $user["uid"]) {
                $newUserList[$userTmp["uid"]] = $userTmp;
            } else {
                $changed = true;
            }
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
144

145
        if ($changed) {
Dominik Hebeler's avatar
Dominik Hebeler committed
146 147 148 149 150
            if (sizeof($newUserList) > 0) {
                Cache::put(HumanVerification::PREFIX . "." . $user["id"], $newUserList, now()->addWeeks(2));
            } else {
                Cache::forget(HumanVerification::PREFIX . "." . $user["id"], $newUserList);
            }
151 152 153
        }
    }

154 155
    private static function removeUser($request, $uid)
    {
Dominik Hebeler's avatar
Dominik Hebeler committed
156 157
        $ip = $request->ip();
        $id = "";
Dominik Hebeler's avatar
Bugfix  
Dominik Hebeler committed
158
        if (HumanVerification::couldBeSpammer($ip)) {
Dominik Hebeler's avatar
Dominik Hebeler committed
159
            $id = hash("sha1", "999.999.999.999");
Dominik Hebeler's avatar
Dominik Hebeler committed
160
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
161
            $id = hash("sha1", $ip);
Dominik Hebeler's avatar
Dominik Hebeler committed
162
        }
163

164 165 166 167 168 169 170
        $userlist = Cache::get(HumanVerification::PREFIX . "." . $id, []);
        $user = null;

        if (sizeof($userlist) === 0 || empty($userlist[$uid])) {
            return;
        } else {
            $user = $userlist[$uid];
171 172 173
        }

        $sum = 0;
174
        foreach ($userlist as $uidTmp => $userTmp) {
Dominik Hebeler's avatar
Dominik Hebeler committed
175
            if (!empty($userTmp) && gettype($userTmp["whitelist"]) === "boolean" && !$userTmp["whitelist"]) {
176 177
                $sum += intval($userTmp["unusedResultPages"]);
            }
178 179
        }
        # Check if we have to whitelist the user or if we can simply delete the data
180
        if ($user["unusedResultPages"] < $sum && !$user["whitelist"]) {
181
            # Whitelist
182
            $user["whitelist"] = true;
183 184
        }

185
        if ($user["whitelist"]) {
186 187
            $user["unusedResultPages"] = 0;
            HumanVerification::saveUser($user);
188
        } else {
189
            HumanVerification::deleteUser($user);
190 191 192
        }
    }

193 194
    private static function checkId($request, $id)
    {
Dominik Hebeler's avatar
Dominik Hebeler committed
195 196
        $uid = "";
        $ip = $request->ip();
Dominik Hebeler's avatar
Bugfix  
Dominik Hebeler committed
197
        if (HumanVerification::couldBeSpammer($ip)) {
Dominik Hebeler's avatar
Dominik Hebeler committed
198
            $uid = hash("sha1", "999.999.999.999" . $ip . $_SERVER["AGENT"] . "uid");
Dominik Hebeler's avatar
Dominik Hebeler committed
199
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
200
            $uid = hash("sha1", $ip . $_SERVER["AGENT"] . "uid");
Dominik Hebeler's avatar
Dominik Hebeler committed
201 202 203
        }

        if ($uid === $id) {
Dominik Hebeler's avatar
Dominik Hebeler committed
204
            return true;
205
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
206 207 208
            return false;
        }
    }
Dominik Hebeler's avatar
Dominik Hebeler committed
209

Dominik Hebeler's avatar
Bugfix  
Dominik Hebeler committed
210
    public static function couldBeSpammer($ip)
Dominik Hebeler's avatar
Dominik Hebeler committed
211
    {
Dominik Hebeler's avatar
Bugfix  
Dominik Hebeler committed
212 213
        # Check for recent Spams
        $eingabe = \Request::input('eingabe');
214 215 216 217 218
        $spams = Redis::lrange("spam", 0, -1);
        foreach ($spams as $spam) {
            if (\preg_match($spam, $eingabe)) {
                return true;
            }
Dominik Hebeler's avatar
Bugfix  
Dominik Hebeler committed
219 220
        }

221
        return false;
Dominik Hebeler's avatar
Dominik Hebeler committed
222
    }
223

Dominik Hebeler's avatar
Dominik Hebeler committed
224 225
    public function botOverview(Request $request)
    {
226 227 228 229
        $id = "";
        $uid = "";
        $ip = $request->ip();
        if (\App\Http\Controllers\HumanVerification::couldBeSpammer($ip)) {
Dominik Hebeler's avatar
Dominik Hebeler committed
230 231
            $id = hash("sha1", "999.999.999.999");
            $uid = hash("sha1", "999.999.999.999" . $ip . $_SERVER["AGENT"] . "uid");
232
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
233 234
            $id = hash("sha1", $ip);
            $uid = hash("sha1", $ip . $_SERVER["AGENT"] . "uid");
235 236 237 238 239 240 241 242
        }

        $userList = Cache::get(HumanVerification::PREFIX . "." . $id);
        $user = $userList[$uid];

        return view('humanverification.botOverview')
            ->with('title', "Bot Overview")
            ->with('ip', $ip)
243
            ->with('userList', $userList)
244 245 246
            ->with('user', $user);
    }

Dominik Hebeler's avatar
Dominik Hebeler committed
247 248
    public function botOverviewChange(Request $request)
    {
249 250 251 252
        $id = "";
        $uid = "";
        $ip = $request->ip();
        if (\App\Http\Controllers\HumanVerification::couldBeSpammer($ip)) {
Dominik Hebeler's avatar
Dominik Hebeler committed
253 254
            $id = hash("sha1", "999.999.999.999");
            $uid = hash("sha1", "999.999.999.999" . $ip . $_SERVER["AGENT"] . "uid");
255
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
256 257
            $id = hash("sha1", $ip);
            $uid = hash("sha1", $ip . $_SERVER["AGENT"] . "uid");
258 259 260 261 262
        }

        $userList = Cache::get(HumanVerification::PREFIX . "." . $id);
        $user = $userList[$uid];

Dominik Hebeler's avatar
Dominik Hebeler committed
263
        if ($request->filled("locked")) {
264
            $user["locked"] = boolval($request->input('locked'));
Dominik Hebeler's avatar
Dominik Hebeler committed
265
        } elseif ($request->filled("whitelist")) {
266
            $user["whitelist"] = boolval($request->input('whitelist'));
Dominik Hebeler's avatar
Dominik Hebeler committed
267
        } elseif ($request->filled("unusedResultPages")) {
268 269 270 271 272 273
            $user["unusedResultPages"] = intval($request->input('unusedResultPages'));
        }

        HumanVerification::saveUser($user);
        return redirect('admin/bot');
    }
Dominik Hebeler's avatar
Dominik Hebeler committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353

    public function browserVerification(Request $request)
    {
        $key = $request->input("id", "");

        // Verify that key is a md5 checksum
        if (!preg_match("/^[a-f0-9]{32}$/", $key)) {
            abort(404);
        }

        Redis::connection("cache")->pipeline(function ($redis) use ($key) {
            $redis->rpush($key, true);
            $redis->expire($key, 30);
        });

        return response("", 200)->header("Content-Type", "text/css");
    }

    public static function block(Request $request)
    {
        $prefix = "humanverification";

        $ip = $request->ip();
        $id = "";
        $uid = "";
        if (\App\Http\Controllers\HumanVerification::couldBeSpammer($ip)) {
            $id = hash("sha1", "999.999.999.999");
            $uid = hash("sha1", "999.999.999.999" . $ip . $_SERVER["AGENT"] . "uid");
        } else {
            $id = hash("sha1", $ip);
            $uid = hash("sha1", $ip . $_SERVER["AGENT"] . "uid");
        }

        /**
         * 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') || Cookie::get('key') !== null || $request->filled('appversion') || !env('BOT_PROTECTION', false)) {
            $update = false;
            return $next($request);
        }

        # Get all Users of this IP
        $users = Cache::get($prefix . "." . $id, []);

        $user = [];
        $changed = false;
        if (empty($users[$uid])) {
            $user = [
                'uid' => $uid,
                'id' => $id,
                'unusedResultPages' => 0,
                'whitelist' => false,
                'locked' => true,
                "lockedKey" => "",
                "expiration" => now()->addWeeks(2),
            ];
            $changed = true;
        } else {
            $user = $users[$uid];
            if (!$user["locked"]) {
                $user["locked"] = true;
                $changed = true;
            }
        }

        if ($user["whitelist"]) {
            $user["expiration"] = now()->addWeeks(2);
        } else {
            $user["expiration"] = now()->addHours(72);
        }
        if ($changed) {
            $userList = Cache::get($prefix . "." . $user["id"], []);
            $userList[$user["uid"]] = $user;
            Cache::put($prefix . "." . $user["id"], $userList, 2 * 7 * 24 * 60 * 60);
        }
        return [$id, $uid];
    }
Dominik Hebeler's avatar
Dominik Hebeler committed
354
}