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

namespace App\Http\Middleware;

5
6
use Captcha;
use Carbon;
Dominik Hebeler's avatar
Dominik Hebeler committed
7
8
use Closure;
use DB;
9
use Illuminate\Http\Response;
Dominik Hebeler's avatar
Dominik Hebeler committed
10
use URL;
Dominik Hebeler's avatar
Dominik Hebeler committed
11
12
13
14
15
16
17
18
19
20
21
22

class HumanVerification
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
23
24
25
        // The specific user
        $user = null;
        $newUser = true;
26
        $update = true;
Dominik Hebeler's avatar
Dominik Hebeler committed
27
28
29
30
31
32
33
34
35
36
37
38
        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)) {
39
                $update = false;
Dominik Hebeler's avatar
Dominik Hebeler committed
40
41
                return $next($request);
            }
42

43
            $users = DB::select('select * from humanverification where id = ?', [$id]);
44

45
46
47
            # Lock out everyone in a Bot network
            # Find out how many requests this IP has made
            $sum = 0;
48
49
            foreach ($users as $userTmp) {
                if ($uid == $userTmp->uid) {
50
                    $user = ['uid' => $userTmp->uid,
51
52
53
54
55
56
57
58
                        'id' => $userTmp->id,
                        'unusedResultPages' => intval($userTmp->unusedResultPages),
                        'whitelist' => filter_var($userTmp->whitelist, FILTER_VALIDATE_BOOLEAN),
                        'whitelistCounter' => $userTmp->whitelistCounter,
                        'locked' => filter_var($userTmp->locked, FILTER_VALIDATE_BOOLEAN),
                        "lockedKey" => $userTmp->lockedKey,
                        'updated_at' => Carbon::now(),
                    ];
59
60
                    $newUser = false;
                }
61
                if ($userTmp->whitelist === 0) {
62
                    $sum += $userTmp->unusedResultPages;
63
64
                }

65
            }
Dominik Hebeler's avatar
Dominik Hebeler committed
66
67
68
            # If this user doesn't have an entry we will create one

            if ($user === null) {
69
                $user =
Dominik Hebeler's avatar
Dominik Hebeler committed
70
                    [
71
72
73
74
75
76
77
78
79
                    'uid' => $uid,
                    'id' => $id,
                    'unusedResultPages' => 0,
                    'whitelist' => false,
                    'whitelistCounter' => 0,
                    'locked' => false,
                    "lockedKey" => "",
                    'updated_at' => Carbon::now(),
                ];
Dominik Hebeler's avatar
Dominik Hebeler committed
80
            }
81

Dominik Hebeler's avatar
Dominik Hebeler committed
82
83
84
85
86
87
88
89
90
91
92
            # 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;
                }
Dominik Hebeler's avatar
Dominik Hebeler committed
93

Dominik Hebeler's avatar
Dominik Hebeler committed
94
            }
95

Dominik Hebeler's avatar
Dominik Hebeler committed
96
            // Defines if this is the only user using that IP Adress
97
            $alone = true;
98
99
            foreach ($users as $userTmp) {
                if ($userTmp->uid != $uid && !$userTmp->whitelist) {
100
                    $alone = false;
101
102
                }

103
104
105
            }
            if ((!$alone && $sum >= 50 && !$user["whitelist"]) || $refererLock) {
                $user["locked"] = true;
Dominik Hebeler's avatar
Dominik Hebeler committed
106
            }
107

Dominik Hebeler's avatar
Dominik Hebeler committed
108
            # If the user is locked we will force a Captcha validation
109
            if ($user["locked"]) {
Dominik Hebeler's avatar
Dominik Hebeler committed
110
                $captcha = Captcha::create("default", true);
111
                $user["lockedKey"] = $captcha["key"];
Dominik Hebeler's avatar
Dominik Hebeler committed
112
113
114
115
116
117
118
119
120
                return
                new Response(
                    view('humanverification.captcha')
                        ->with('title', "Bestätigung erforderlich")
                        ->with('id', $uid)
                        ->with('url', url()->full())
                        ->with('image', $captcha["img"])
                );
            }
121

122
            $user["unusedResultPages"]++;
Dominik Hebeler's avatar
Dominik Hebeler committed
123

124
            if ($alone || $user["whitelist"]) {
Dominik Hebeler's avatar
Dominik Hebeler committed
125
126
127
128
129
130
131
132
                # 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

133
                if ($user["unusedResultPages"] === 50 || ($user["unusedResultPages"] > 50 && $user["unusedResultPages"] % 25 === 0)) {
134
                    $user["locked"] = true;
Dominik Hebeler's avatar
Dominik Hebeler committed
135
136
137
138
139
                }

            }
        } catch (\Illuminate\Database\QueryException $e) {
            // Failure in contacting metager3.de
140
141
        } finally {
            // Update the user in the database
142
143
            if ($update) {
                if ($newUser) {
144
145
146
147
148
149
150
151
152
153
154
                    DB::table('humanverification')->insert(
                        [
                            'uid' => $user["uid"],
                            'id' => $user["id"],
                            'unusedResultPages' => $user['unusedResultPages'],
                            'whitelist' => $user["whitelist"],
                            'whitelistCounter' => $user["whitelistCounter"],
                            'locked' => $user["locked"],
                            "lockedKey" => $user["lockedKey"],
                            'updated_at' => $user["updated_at"],
                        ]
155
                    );
156
                } else {
157
158
159
160
161
162
163
164
165
166
167
                    DB::table('humanverification')->where('uid', $uid)->update(
                        [
                            'uid' => $user["uid"],
                            'id' => $user["id"],
                            'unusedResultPages' => $user['unusedResultPages'],
                            'whitelist' => $user["whitelist"],
                            'whitelistCounter' => $user["whitelistCounter"],
                            'locked' => $user["locked"],
                            "lockedKey" => $user["lockedKey"],
                            'updated_at' => $user["updated_at"],
                        ]
168
                    );
169
                }
170
            }
Dominik Hebeler's avatar
Dominik Hebeler committed
171
        }
172
        $request->request->add(['verification_id' => $user["uid"], 'verification_count' => $user["unusedResultPages"]]);
Dominik Hebeler's avatar
Dominik Hebeler committed
173
174
175
        return $next($request);
    }
}