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

namespace App\Http\Middleware;

5
use Captcha;
Dominik Hebeler's avatar
Dominik Hebeler committed
6
use Closure;
7
use Cookie;
8
use Illuminate\Http\Response;
9
use Illuminate\Support\Facades\Cache;
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
        // The specific user
        $user = null;
25
        $update = true;
26
        $prefix = "humanverification";
Dominik Hebeler's avatar
Dominik Hebeler committed
27
        try {
Dominik Hebeler's avatar
Dominik Hebeler committed
28
29
30
            $ip = $request->ip();
            $id = "";
            $uid = "";
Dominik Hebeler's avatar
Bugfix    
Dominik Hebeler committed
31
            if (\App\Http\Controllers\HumanVerification::couldBeSpammer($ip)) {
Dominik Hebeler's avatar
Dominik Hebeler committed
32
33
34
35
36
37
                $id = hash("sha512", "999.999.999.999");
                $uid = hash("sha512", "999.999.999.999" . $ip . $_SERVER["AGENT"] . "uid");
            } else {
                $id = hash("sha512", $ip);
                $uid = hash("sha512", $ip . $_SERVER["AGENT"] . "uid");
            }
Dominik Hebeler's avatar
Dominik Hebeler committed
38
39
40
41
42
43
44
45
            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.
             */
46
            if ($request->filled('password') || $request->filled('key') || Cookie::get('key') !== null || $request->filled('appversion') || !env('BOT_PROTECTION', false)) {
47
                $update = false;
Dominik Hebeler's avatar
Dominik Hebeler committed
48
49
                return $next($request);
            }
50

51
            # Get all Users of this IP
52
53
            $users = Cache::get($prefix . "." . $id, []);
            $users = $this->removeOldUsers($users);
54
55

            $user = [];
56
57
            if (empty($users[$uid])) {
                $user = [
58
59
60
61
62
63
                    'uid' => $uid,
                    'id' => $id,
                    'unusedResultPages' => 0,
                    'whitelist' => false,
                    'locked' => false,
                    "lockedKey" => "",
64
                    "expiration" => now()->addWeeks(2),
65
                ];
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
            } else {
                $user = $users[$uid];
            }
            # Lock out everyone in a Bot network
            # Find out how many requests this IP has made
            $sum = 0;
            // Defines if this is the only user using that IP Adress
            $alone = true;
            foreach ($users as $uid => $userTmp) {
                if (!$userTmp["whitelist"]) {
                    $sum += $userTmp["unusedResultPages"];
                    if ($userTmp["uid"] != $uid) {
                        $alone = false;
                    }
                }
Dominik Hebeler's avatar
Dominik Hebeler committed
81
            }
82

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

Dominik Hebeler's avatar
Dominik Hebeler committed
95
            }
96

97
98
            if ((!$alone && $sum >= 50 && !$user["whitelist"]) || $refererLock) {
                $user["locked"] = true;
Dominik Hebeler's avatar
Dominik Hebeler committed
99
            }
100

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

116
            $user["unusedResultPages"]++;
Dominik Hebeler's avatar
Dominik Hebeler committed
117

118
            if ($alone || $user["whitelist"]) {
Dominik Hebeler's avatar
Dominik Hebeler committed
119
120
121
122
                # This IP doesn't need verification yet
                # The user currently isn't locked

                # We have different security gates:
Dominik Hebeler's avatar
Dominik Hebeler committed
123
                #   50 and then every 25 => Captcha validated Result Pages
Dominik Hebeler's avatar
Dominik Hebeler committed
124
                # If the user shows activity on our result page the counter will be deleted
125
                if ($user["unusedResultPages"] === 50 || ($user["unusedResultPages"] > 50 && $user["unusedResultPages"] % 25 === 0)) {
126
                    $user["locked"] = true;
Dominik Hebeler's avatar
Dominik Hebeler committed
127
128
129
                }

            }
130
        } finally {
131
            if ($update) {
132
                if ($user["whitelist"]) {
133
                    $user["expiration"] = now()->addWeeks(2);
134
                } else {
135
                    $user["expiration"] = now()->addHours(72);
136
                }
137
                $this->setUser($prefix, $user);
138
            }
Dominik Hebeler's avatar
Dominik Hebeler committed
139
        }
140

141
        $request->request->add(['verification_id' => $user["uid"], 'verification_count' => $user["unusedResultPages"]]);
Dominik Hebeler's avatar
Dominik Hebeler committed
142
        return $next($request);
Dominik Hebeler's avatar
Dominik Hebeler committed
143

Dominik Hebeler's avatar
Dominik Hebeler committed
144
    }
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

    public function setUser($prefix, $user)
    {
        // Lock must be acquired within 2 seconds
        $userList = Cache::get($prefix . "." . $user["id"], []);
        $userList[$user["uid"]] = $user;
        Cache::put($prefix . "." . $user["id"], $userList, now()->addWeeks(2));
    }

    public function removeOldUsers($userList)
    {
        $newUserlist = [];
        $now = now();

        $id = "";
        $changed = false;
        foreach ($userList as $uid => $user) {
            $id = $user["id"];
            if ($now < $user["expiration"]) {
                $newUserlist[$user["uid"]] = $user;
            } else {
                $changed = true;
            }
        }

        if ($changed) {
            // Lock must be acquired within 2 seconds
            Cache::lock($prefix . "." . $user["id"])->block(2, function () {
                Cache::put($prefix . "." . $user["id"], $newUserlist, now()->addWeeks(2));
            });
        }

        return $newUserlist;
    }
Dominik Hebeler's avatar
Dominik Hebeler committed
179
}