Merge pull request #320 from wrongecho/brute-force-login

Add basic IP login brute force protection
This commit is contained in:
Johnny 2022-01-22 16:45:36 -05:00 committed by GitHub
commit c47eac328d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 140 additions and 117 deletions

245
login.php
View File

@ -1,8 +1,8 @@
<?php <?php
if(!file_exists('config.php')){ if(!file_exists('config.php')){
header("Location: setup.php"); header("Location: setup.php");
exit; exit;
} }
include("config.php"); include("config.php");
@ -26,96 +26,119 @@ if($config_https_only){
if(isset($_POST['login'])){ if(isset($_POST['login'])){
// Sessions should start after the user has POSTed data // Sessions should start after the user has POSTed data
session_start(); session_start();
$email = strip_tags(mysqli_real_escape_string($mysqli,$_POST['email'])); // Check recent failed login attempts for this IP (more than 10 failed logins in 5 mins)
$password = $_POST['password'];
$current_code = strip_tags(mysqli_real_escape_string($mysqli,$_POST['current_code']));
if(!empty($current_code)){
$current_code = strip_tags(mysqli_real_escape_string($mysqli,$_POST['current_code']));
}
$sql = mysqli_query($mysqli,"SELECT * FROM users WHERE user_email = '$email'");
$row = mysqli_fetch_array($sql);
if(password_verify($password, $row['user_password'])){ // TODO: We can probably just use a count for this, but couldn't make it not count *everything*
$ip_failed_logins_sql = mysqli_query($mysqli, "SELECT * FROM logs WHERE log_ip = '$ip' AND log_type = 'Login' AND log_action = 'Failed' AND log_created_at > (NOW() - INTERVAL 5 MINUTE)");
$failed_login_count = mysqli_num_rows($ip_failed_logins_sql);
$token = $row['user_token']; // Login brute force check
$_SESSION['user_id'] = $row['user_id']; if($failed_login_count >= 10){
$_SESSION['user_name'] = $row['user_name'];
$user_name = $row['user_name'];
$user_id = $row['user_id'];
// Setup encryption session key // Logging
if(isset($row['user_specific_encryption_ciphertext'])){ mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = 'Failed', log_description = 'Failed login attempt due to IP lockout', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW()");
$user_encryption_ciphertext = $row['user_specific_encryption_ciphertext'];
$site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password); // Send an alert only count hits 10 to reduce flooding alerts (using 1 as "default" company)
generateUserSessionKey($site_encryption_master_key); if($failed_login_count == 10){
mysqli_query($mysqli,"INSERT INTO alerts SET alert_type = 'Lockout', alert_message = '$ip was locked out for repeated failed login attempts.', alert_date = NOW(), company_id = '1'");
}
// Inform user
$response = '<div class=\'alert alert-danger\'>IP Lockout - Please try again later.<button class=\'close\' data-dismiss=\'alert\'>&times;</button></div>';
} }
// Setup extension // Passed login brute force check
if(isset($row['user_extension_key']) && !empty($row['user_extension_key'])){ else{
// Extension cookie $email = strip_tags(mysqli_real_escape_string($mysqli, $_POST['email']));
// Note: Browsers don't accept cookies with SameSite None if they are not HTTPS. $password = $_POST['password'];
setcookie("user_extension_key", "$row[user_extension_key]", ['path' => '/','secure' => true,'httponly' => true,'samesite' => 'None']); $current_code = strip_tags(mysqli_real_escape_string($mysqli, $_POST['current_code']));
if (!empty($current_code)) {
$current_code = strip_tags(mysqli_real_escape_string($mysqli, $_POST['current_code']));
}
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_email = '$email'");
$row = mysqli_fetch_array($sql);
if (password_verify($password, $row['user_password'])) {
// Set PHP session in DB so we can access the session encryption data (above) $token = $row['user_token'];
$user_php_session = session_id(); $_SESSION['user_id'] = $row['user_id'];
mysqli_query($mysqli, "UPDATE users SET user_php_session = '$user_php_session' WHERE user_id = '$user_id'"); $_SESSION['user_name'] = $row['user_name'];
$user_name = $row['user_name'];
$user_id = $row['user_id'];
} // Setup encryption session key
if (isset($row['user_specific_encryption_ciphertext'])) {
$user_encryption_ciphertext = $row['user_specific_encryption_ciphertext'];
$site_encryption_master_key = decryptUserSpecificKey($user_encryption_ciphertext, $password);
generateUserSessionKey($site_encryption_master_key);
}
if(empty($token)){ // Setup extension
$_SESSION['logged'] = TRUE; if (isset($row['user_extension_key']) && !empty($row['user_extension_key'])) {
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login', log_action = 'Success', log_description = '$user_name successfully logged in', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id"); // Extension cookie
// Note: Browsers don't accept cookies with SameSite None if they are not HTTPS.
setcookie("user_extension_key", "$row[user_extension_key]", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']);
header("Location: dashboard.php"); // Set PHP session in DB so we can access the session encryption data (above)
}else{ $user_php_session = session_id();
$token_field = "<div class='input-group mb-3'> mysqli_query($mysqli, "UPDATE users SET user_php_session = '$user_php_session' WHERE user_id = '$user_id'");
<input type='text' class='form-control' placeholder='Token' name='current_code' autofocus>
<div class='input-group-append'> }
<div class='input-group-text'>
<span class='fas fa-key'></span> if (empty($token)) {
$_SESSION['logged'] = TRUE;
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = 'Success', log_description = '$user_name successfully logged in', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id");
header("Location: dashboard.php");
} else {
$token_field = "<div class='input-group mb-3'>
<input type='text' class='form-control' placeholder='Token' name='current_code' autofocus>
<div class='input-group-append'>
<div class='input-group-text'>
<span class='fas fa-key'></span>
</div>
</div>
</div>";
require_once("rfc6238.php");
if (TokenAuth6238::verify($token, $current_code)) {
$_SESSION['logged'] = TRUE;
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login 2FA', log_action = 'Success', log_description = '$user_name successfully logged in using 2FA', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id");
//header("Location: $config_start_page");
header("Location: dashboard.php");
} else {
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = '2FA Failed', log_description = '$user_name failed 2FA', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id");
$response = "
<div class='alert alert-primary'>
Please Enter 2FA Key!
<button class='close' data-dismiss='alert'>&times;</button>
</div> </div>
</div> ";
</div>"; }
}
require_once("rfc6238.php"); } else {
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Login', log_action = 'Failed', log_description = 'Failed login attempt using $email', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW()");
if(TokenAuth6238::verify($token,$current_code)){ $response = "
$_SESSION['logged'] = TRUE; <div class='alert alert-danger'>
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login 2FA', log_action = 'Success', log_description = '$user_name successfully logged in using 2FA', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id"); Incorrect username or password.
//header("Location: $config_start_page");
header("Location: dashboard.php");
}else{
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login', log_action = '2FA Failed', log_description = '$user_name failed 2FA', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW(), log_user_id = $user_id");
$response = "
<div class='alert alert-primary'>
Please Enter 2FA Key!
<button class='close' data-dismiss='alert'>&times;</button> <button class='close' data-dismiss='alert'>&times;</button>
</div> </div>
"; ";
} }
} }
}else{
mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login', log_action = 'Failed', log_description = 'Failed login attempt using $email', log_ip = '$ip', log_user_agent = '$user_agent', log_created_at = NOW()");
$response = "
<div class='alert alert-danger'>
Incorrect username or password.
<button class='close' data-dismiss='alert'>&times;</button>
</div>
";
}
} }
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title><?php echo $config_app_name; ?> | Login</title> <title><?php echo $config_app_name; ?> | Login</title>
@ -128,63 +151,63 @@ if(isset($_POST['login'])){
<link rel="stylesheet" href="dist/css/adminlte.min.css"> <link rel="stylesheet" href="dist/css/adminlte.min.css">
<!-- Google Font: Source Sans Pro --> <!-- Google Font: Source Sans Pro -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
</head> </head>
<body class="hold-transition login-page"> <body class="hold-transition login-page">
<div class="login-box"> <div class="login-box">
<div class="login-logo"> <div class="login-logo">
<b>IT</b>Flow <b>IT</b>Flow
</div> </div>
<!-- /.login-logo --> <!-- /.login-logo -->
<div class="card"> <div class="card">
<div class="card-body login-card-body"> <div class="card-body login-card-body">
<p class="login-box-msg"><?php if(isset($response)) { echo $response; } ?></p> <p class="login-box-msg"><?php if(isset($response)) { echo $response; } ?></p>
<form method="post"> <form method="post">
<div class="input-group mb-3"> <div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Email" name="email" value="<?php if(!empty($token_field)){ echo $email; }?>" required <?php if(empty($token_field)){ echo "autofocus"; } ?> > <input type="text" class="form-control" placeholder="Email" name="email" value="<?php if(!empty($token_field)){ echo $email; }?>" required <?php if(empty($token_field)){ echo "autofocus"; } ?> >
<div class="input-group-append"> <div class="input-group-append">
<div class="input-group-text"> <div class="input-group-text">
<span class="fas fa-envelope"></span> <span class="fas fa-envelope"></span>
</div> </div>
</div> </div>
</div> </div>
<div class="input-group mb-3"> <div class="input-group mb-3">
<input type="password" class="form-control" placeholder="Password" name="password" value="<?php if(!empty($token_field)){ echo $password; } ?>" required> <input type="password" class="form-control" placeholder="Password" name="password" value="<?php if(!empty($token_field)){ echo $password; } ?>" required>
<div class="input-group-append"> <div class="input-group-append">
<div class="input-group-text"> <div class="input-group-text">
<span class="fas fa-lock"></span> <span class="fas fa-lock"></span>
</div> </div>
</div> </div>
</div> </div>
<?php if(!empty($token_field)){ echo $token_field; } ?> <?php if(!empty($token_field)){ echo $token_field; } ?>
<button type="submit" class="btn btn-primary btn-block mb-3" name="login">Sign In</button> <button type="submit" class="btn btn-primary btn-block mb-3" name="login">Sign In</button>
</form> </form>
</div> </div>
<!-- /.login-card-body --> <!-- /.login-card-body -->
</div> </div>
</div> </div>
<!-- /.login-box --> <!-- /.login-box -->
<!-- jQuery --> <!-- jQuery -->
<script src="plugins/jquery/jquery.min.js"></script> <script src="plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 --> <!-- Bootstrap 4 -->
<script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- AdminLTE App --> <!-- AdminLTE App -->
<script src="dist/js/adminlte.min.js"></script> <script src="dist/js/adminlte.min.js"></script>
<script src="plugins/Show-Hide-Passwords-Bootstrap-4/bootstrap-show-password.min.js"></script> <script src="plugins/Show-Hide-Passwords-Bootstrap-4/bootstrap-show-password.min.js"></script>
<!-- Prevents resubmit on refresh or back --> <!-- Prevents resubmit on refresh or back -->
<script> <script>
if(window.history.replaceState){ if(window.history.replaceState){
window.history.replaceState(null,null,window.location.href); window.history.replaceState(null,null,window.location.href);
} }
</script> </script>
</body> </body>
</html> </html>