Browse Source

Slimmer bruteforce prevention

spaghetti 9 years ago
parent
commit
d0225b89d0

+ 18
- 76
sections/login/index.php View File

@@ -180,7 +180,7 @@ if (isset($_REQUEST['act']) && $_REQUEST['act'] == 'recover') {
180 180
 
181 181
     // Either a form for the user's email address, or a success message
182 182
     require('recover_step1.php');
183
-  } // End if (step 1)
183
+  }
184 184
 
185 185
 } // End password recovery
186 186
 
@@ -189,81 +189,24 @@ else {
189 189
   $Validate->SetFields('username', true, 'regex', 'You did not enter a valid username.', array('regex' => USERNAME_REGEX));
190 190
   $Validate->SetFields('password', '1', 'string', 'You entered an invalid password.', array('minlength' => '6', 'maxlength' => '307200'));
191 191
 
192
-  $DB->query("
193
-    SELECT ID, Attempts, Bans, BannedUntil
194
-    FROM login_attempts
195
-    WHERE IP = '".db_string($_SERVER['REMOTE_ADDR'])."'");
196
-  // Todo: handle IP encryption
197
-  list($AttemptID, $Attempts, $Bans, $BannedUntil) = $DB->next_record();
192
+  list($Attempts, $Banned) = $Cache->get_value('login_attempts_'.db_string($_SERVER['REMOTE_ADDR']));
198 193
 
199 194
   // Function to log a user's login attempt
200
-  function log_attempt($UserID) {
201
-    global $DB, $Cache, $AttemptID, $Attempts, $Bans, $BannedUntil;
202
-    if (!apc_exists('DBKEY')) { return; }
203
-    $IPStr = $_SERVER['REMOTE_ADDR'];
204
-    $IPA = substr($IPStr, 0, strcspn($IPStr, '.'));
205
-    $IP = Tools::ip_to_unsigned($IPStr);
206
-    if ($AttemptID) { // User has attempted to log in recently
207
-      $Attempts++;
208
-      if ($Attempts > 5) { // Only 6 allowed login attempts, ban user's IP
209
-        $BannedUntil = time_plus(60 * 60 * 6);
210
-        $DB->query("
211
-          UPDATE login_attempts
212
-          SET
213
-            LastAttempt = '".sqltime()."',
214
-            Attempts = '".db_string($Attempts)."',
215
-            BannedUntil = '".db_string($BannedUntil)."',
216
-            Bans = Bans + 1
217
-          WHERE ID = '".db_string($AttemptID)."'");
218
-
219
-        if ($Bans > 9) { // Automated bruteforce prevention
220
-          $DB->query("
221
-            SELECT Reason
222
-            FROM ip_bans
223
-            WHERE $IP BETWEEN FromIP AND ToIP");
224
-          if ($DB->has_results()) {
225
-            //Ban exists already, only add new entry if not for same reason
226
-            list($Reason) = $DB->next_record(MYSQLI_BOTH, false);
227
-            if ($Reason != 'Automated ban per >60 failed login attempts') {
228
-              $DB->query("
229
-                UPDATE ip_bans
230
-                SET Reason = CONCAT('Automated ban per >60 failed login attempts AND ', Reason)
231
-                WHERE FromIP = $IP
232
-                  AND ToIP = $IP");
233
-            }
234
-          } else {
235
-            //No ban
236
-            $DB->query("
237
-              INSERT IGNORE INTO ip_bans
238
-                (FromIP, ToIP, Reason)
239
-              VALUES
240
-                ('".$IP."','".$IP."', 'Automated ban per >60 failed login attempts')");
241
-            $Cache->delete_value("ip_bans_$IPA");
242
-          }
243
-        }
244
-      } else {
245
-        // User has attempted fewer than 6 logins
246
-        $DB->query("
247
-          UPDATE login_attempts
248
-          SET
249
-            LastAttempt = '".sqltime()."',
250
-            Attempts = '".db_string($Attempts)."',
251
-            BannedUntil = '0000-00-00 00:00:00'
252
-          WHERE ID = '".db_string($AttemptID)."'");
253
-      }
254
-    } else { // User has not attempted to log in recently
255
-      $Attempts = 1;
256
-      $DB->query("
257
-        INSERT INTO login_attempts
258
-          (UserID, IP, LastAttempt, Attempts)
259
-        VALUES
260
-          ('".db_string($UserID)."', '".db_string(DBCrypt::encrypt($IPStr))."', '".sqltime()."', 1)");
195
+  function log_attempt() {
196
+    global $DB, $Cache, $Attempts;
197
+    $Attempts = ($Attempts ?? 0) + 1;
198
+    $Cache->cache_value('login_attempts_'.db_string($_SERVER['REMOTE_ADDR']), array($Attempts, ($Attempts > 5)), 60*60*$Attempts);
199
+    $AllAttempts = $Cache->get_value('login_attempts');
200
+    $AllAttempts[$_SERVER['REMOTE_ADDR']] = time()+(60*60*$Attempts);
201
+    foreach($AllAttempts as $IP => $Time) {
202
+      if ($Time < time()) { unset($AllAttempts[$IP]); }
261 203
     }
262
-  } // end log_attempt function
204
+    $Cache->cache_value('login_attempts', $AllAttempts, 0);
205
+  }
263 206
 
264 207
   // If user has submitted form
265 208
   if (isset($_POST['username']) && !empty($_POST['username']) && isset($_POST['password']) && !empty($_POST['password'])) {
266
-    if (strtotime($BannedUntil) > time()) {
209
+    if ($Banned) {
267 210
       header("Location: login.php");
268 211
       die();
269 212
     }
@@ -282,7 +225,7 @@ else {
282 225
         WHERE Username = '".db_string($_POST['username'])."'
283 226
           AND Username != ''");
284 227
       list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Enabled) = $DB->next_record(MYSQLI_NUM, array(2));
285
-      if (strtotime($BannedUntil) < time()) {
228
+      if (!$Banned) {
286 229
         if ($UserID && Users::check_password($_POST['password'], $PassHash)) {
287 230
           // Update hash if better algorithm available
288 231
           if (password_needs_rehash($PassHash, PASSWORD_DEFAULT)) {
@@ -303,7 +246,6 @@ else {
303 246
               setcookie('session', $Cookie, 0, '/', '', true, true);
304 247
             }
305 248
 
306
-            //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that
307 249
             // Because we <3 our staff
308 250
             $Permissions = Permissions::get_permissions($PermissionID);
309 251
             $CustomPermissions = unserialize($CustomPermissions);
@@ -348,7 +290,7 @@ else {
348 290
               die();
349 291
             }
350 292
           } else {
351
-            log_attempt($UserID);
293
+            log_attempt();
352 294
             if ($Enabled == 2) {
353 295
 
354 296
               // Save the username in a cookie for the disabled page
@@ -360,19 +302,19 @@ else {
360 302
             setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false);
361 303
           }
362 304
         } else {
363
-          log_attempt($UserID);
305
+          log_attempt();
364 306
 
365 307
           $Err = 'Your username or password was incorrect.';
366 308
           setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false);
367 309
         }
368 310
 
369 311
       } else {
370
-        log_attempt($UserID);
312
+        log_attempt();
371 313
         setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false);
372 314
       }
373 315
 
374 316
     } else {
375
-      log_attempt('0');
317
+      log_attempt();
376 318
       setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false);
377 319
     }
378 320
   }

+ 2
- 10
sections/login/login.php View File

@@ -2,18 +2,10 @@
2 2
   <span id="no-cookies" class="hidden warning">You appear to have cookies disabled.<br /><br /></span>
3 3
   <noscript><span class="warning"><?=SITE_NAME?> requires JavaScript to function properly. Please enable JavaScript in your browser.</span><br /><br /></noscript>
4 4
 <?
5
-if (strtotime($BannedUntil) < time()) {
5
+if (!$Banned) {
6 6
 ?>
7 7
   <form class="auth_form" name="login" id="loginform" method="post" action="login.php">
8 8
 <?
9
-
10
-  if (!empty($BannedUntil) && $BannedUntil != '0000-00-00 00:00:00') {
11
-    $DB->query("
12
-      UPDATE login_attempts
13
-      SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0'
14
-      WHERE ID = '".db_string($AttemptID)."'");
15
-    $Attempts = 0;
16
-  }
17 9
   if (isset($Err)) {
18 10
 ?>
19 11
   <span class="warning"><?=$Err?><br /><br /></span>
@@ -48,7 +40,7 @@ if (strtotime($BannedUntil) < time()) {
48 40
 <?
49 41
 } else {
50 42
 ?>
51
-  <span class="warning">You are banned from logging in for another <?=time_diff($BannedUntil)?>.</span>
43
+  <span class="warning">You are banned from logging in for a few hours.</span>
52 44
 <?
53 45
 }
54 46
 

+ 0
- 11
sections/schedule/hourly/lower_login_attempts.php View File

@@ -1,11 +0,0 @@
1
-<?
2
-//------------- Lower Login Attempts ------------------------------------//
3
-
4
-$DB->query("
5
-  UPDATE login_attempts
6
-  SET Attempts = Attempts - 1
7
-  WHERE Attempts > 0");
8
-$DB->query("
9
-  DELETE FROM login_attempts
10
-  WHERE LastAttempt < '".time_minus(3600 * 24 * 90)."'");
11
-?>

+ 19
- 25
sections/tools/managers/login_watch.php View File

@@ -3,27 +3,22 @@ if (!check_perms('admin_login_watch')) {
3 3
   error(403);
4 4
 }
5 5
 
6
-if (isset($_POST['submit']) && isset($_POST['id']) && $_POST['submit'] == 'Unban' && is_number($_POST['id'])) {
6
+if (isset($_POST['submit']) && isset($_POST['ip']) && $_POST['submit'] == 'Unban') {
7 7
   authorize();
8
-  $DB->query('
9
-    DELETE FROM login_attempts
10
-    WHERE ID = '.$_POST['id']);
8
+  $Cache->delete_value('login_attempts_'.$_POST['ip']);
11 9
 }
12 10
 
13 11
 View::show_header('Login Watch');
14 12
 
15
-$DB->query('
16
-  SELECT
17
-    ID,
18
-    IP,
19
-    UserID,
20
-    LastAttempt,
21
-    Attempts,
22
-    BannedUntil,
23
-    Bans
24
-  FROM login_attempts
25
-  WHERE BannedUntil > "'.sqltime().'"
26
-  ORDER BY BannedUntil ASC');
13
+$AttemptIPs = $Cache->get_value('login_attempts');
14
+$AllAttempts = array();
15
+foreach($AttemptIPs as $IP => $Time) {
16
+  if (time() > $Time) { continue; }
17
+  list($Attempts, $Banned) = $Cache->get_value('login_attempts_'.$IP);
18
+  if (!isset($Attempts) && !isset($Banned)) { continue; }
19
+  $AllAttempts[] = [$IP, $Attempts, $Banned, $Time];
20
+}
21
+
27 22
 ?>
28 23
 <div class="thin">
29 24
   <div class="header">
@@ -32,26 +27,26 @@ $DB->query('
32 27
   <table width="100%">
33 28
     <tr class="colhead">
34 29
       <td>IP</td>
35
-      <td>User</td>
36
-      <td>Bans</td>
37
-      <td>Remaining</td>
30
+      <td>Attempts</td>
31
+      <td>Banned</td>
32
+      <td>Time</td>
38 33
       <td>Submit</td>
39 34
 <?  if (check_perms('admin_manage_ipbans')) { ?>
40 35
       <td>Submit</td>
41 36
 <?  } ?>
42 37
     </tr>
43 38
 <?
44
-while (list($ID, $IP, $UserID, $LastAttempt, $Attempts, $BannedUntil, $Bans) = $DB->next_record()) {
39
+while (list($IP, $Attempts, $Banned, $BannedUntil) = array_shift($AllAttempts)) {
45 40
 ?>
46 41
     <tr class="row">
47 42
       <td>
48 43
         <?=$IP?>
49 44
       </td>
50 45
       <td>
51
-        <? if ($UserID != 0) { echo Users::format_username($UserID, true, true, true, true); } ?>
46
+        <?=$Attempts?>
52 47
       </td>
53 48
       <td>
54
-        <?=$Bans?>
49
+        <?=($Banned?'Yes':'No')?>
55 50
       </td>
56 51
       <td>
57 52
         <?=time_diff($BannedUntil)?>
@@ -59,7 +54,7 @@ while (list($ID, $IP, $UserID, $LastAttempt, $Attempts, $BannedUntil, $Bans) = $
59 54
       <td>
60 55
         <form class="manage_form" name="bans" action="" method="post">
61 56
           <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" />
62
-          <input type="hidden" name="id" value="<?=$ID?>" />
57
+          <input type="hidden" name="ip" value="<?=$IP?>" />
63 58
           <input type="hidden" name="action" value="login_watch" />
64 59
           <input type="submit" name="submit" value="Unban" />
65 60
         </form>
@@ -68,11 +63,10 @@ while (list($ID, $IP, $UserID, $LastAttempt, $Attempts, $BannedUntil, $Bans) = $
68 63
       <td>
69 64
         <form class="manage_form" name="bans" action="" method="post">
70 65
           <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" />
71
-          <input type="hidden" name="id" value="<?=$ID?>" />
72 66
           <input type="hidden" name="action" value="ip_ban" />
73 67
           <input type="hidden" name="start" value="<?=$IP?>" />
74 68
           <input type="hidden" name="end" value="<?=$IP?>" />
75
-          <input type="hidden" name="notes" value="Banned per <?=$Bans?> bans on login watch." />
69
+          <input type="hidden" name="notes" value="Banned per <?=$Attempts?> bans on login watch." />
76 70
           <input type="submit" name="submit" value="IP Ban" />
77 71
         </form>
78 72
       </td>

Loading…
Cancel
Save