douxi7219 2019-01-26 00:49
浏览 95

动态更改html表单操作? (节流策略)

I've browsed multiple Q&A regarding failed login attempts and throttling users based on the amount of invalid-password logins they've attempted.

What exactly is meant by 'blocking' user attempts? How is it done? By pointing the form action to a different page?

I may be missing the obvious, so I tried coming up with a way to 'block' user attempts if they try to log in over x times: change the login form's action to point to another page which does not go to the database at all, it just says something like 'try again in 15 minutes'.

My question is, what is meant by 'blocking users out' and how/where can I get this information? It seems like something which was already done, so it feels like I'm just trying to reinvent the wheel by my own method.

This is the full code in PHP, and a lot of the credits go to a combination of different answers on StackOverflow, as well as some of my own:

<div class="nav-login">
        <?php
            //check if user is logged in
            if (isset($_SESSION['u_id'])) {
                echo '<form action="includes/logout.inc.php" method="POST">
                    <button type="submit" name="submit">Logout</button>
                </form>';
            }
            //creat a default form action which processes the login form normally
            $form_action = 'includes/login.inc.php';

            //if the above page responded with an error in the url, which looks something like login?pwd=error&uid=admin123
            if (isset($_GET['pwd']) && isset($_GET['uid']) && $_GET['pwd'] === 'error') {
                //create empty variables which can be used later on
                $response = '';
                $captcha = '';
                //get the username which was attempted
                $uid = $_GET['uid'];

                //insert the failed logins into a database along with his IP address 
                //(note: learn about and use prepared statements here!)
                mysqli_query($conn, "INSERT INTO failed_logins SET username = '$uid', ip_address = INET_ATON('".get_client_ip()."'), attempted = CURRENT_TIMESTAMP;") or die(mysqli_error($conn));

                // array of throttling
                $throttle = array(
                    //attempts => delay
                    10 => 7000, //secs
                    20 => 12000, //secs
                    30 => 'recaptcha'); //if it reaches this level, only an email link can get them back into their account, where they can either reset password or login through that link.

                // retrieve the latest failed login attempts
                $sql_1 = 'SELECT MAX(attempted) AS attempted FROM failed_logins';
                $result_1 = mysqli_query($conn, $sql_1) or die(mysqli_error($conn));
                if (mysqli_num_rows($result_1) > 0) {
                    $row_1 = mysqli_fetch_assoc($result_1);
                    //if more than 0 results appear, record the latest attempt timestamp inside a variable
                    $latest_attempt = (int) date('U', strtotime($row_1['attempted']));

                    // get the number of failed attempts
                    $sql_1 = 'SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute)';
                    $result_1 = mysqli_query($conn, $sql_1) or die(mysqli_error($conn));
                    if (mysqli_num_rows($result_1) > 0) {
                        // get the returned row
                        $row_1 = mysqli_fetch_assoc($result_1);
                        $failed_attempts = (int) $row_1['failed'];

                        // assume the number of failed attempts was stored in $failed_attempts
                        krsort($throttle);
                        foreach ($throttle as $attempts => $delay) {
                            if ($failed_attempts > $attempts) {
                                // we need to throttle based on delay
                                if (is_numeric($delay)) {

                                    $remaining_delay = time() - $latest_attempt - $delay;

                                    $remaining = abs(number_format($remaining_delay)); //abs() converts a negative number to a positive format. This is because I want to use the timer for a javascript countdown.

                                    // output remaining delay
                                    $response .= '<p id="counting_string">You must wait <span id="counter">' . $remaining . '</span> seconds before your next login attempt.</p>'; 
                                    //Okay, the user got the message, but how do I ban him from clicking login until the countdown is over? (aside from disabling the button via javascript...which seems pretty weak/lame)
                                    //option 1, change the form action?... 
                                    $form_action = "includes/failed_logins.inc.php";

                                } else {
                                    //if the user reaches above 30 attempts, display the captcha. Even if he types in the correct password this time, tho, he won't get to log in, until he clicks on the email link we sent him (with a token, kind of like a reset-password-email-token).
                                    //implement a captcha/recaptcha here
                                    $captcha .= 'captcha!';
                                    //keep the fake form_action
                                    $form_action = "includes/failed_logins.inc.php";
                                    // code to display recaptcha on login form goes here
                                }
                                break;
                            }
                        }        
                    }
                }
            }

            mysqli_close($conn); // Always close your SQL connection and free the memory or you may be needlessly holding a SQL connectio , remember that a shared hosting environment only allows max 30 SQL connections at the same time

            echo '<form action="'.$form_action.'" method="POST">'; // login.inc.php or failed_logins.inc.php 
            echo '
                <input type="text" name="uid" placeholder="Username/e-mail">
                <input type="password" name="pwd" placeholder="password">
                <button type="submit" name="submit" id="login_submit">Login</button>
            </form>';
            if (!empty($response)) {
                echo $response;
            }
            if (!empty($captcha)) {
                echo $captcha;
            }
            echo '<a href="signup.php">Sign up</a>';

            ?>
        </div>

The table looks like this: (i know the ip_address is an int(11) datatype...it's from another StackOverflow answer - it uses INET_ATON for inserts)

+------------+------------------+------+-----+--+
|   Field    |       Type       | Null | Key |  |
+------------+------------------+------+-----+--+
| id         | int(11) unsigned | NO   | PRI |  |
|            | NULL             |      |     |  |
|            | auto_increment   |      |     |  |
| username   | varchar(16)      | NO   |     |  |
|            | NULL             |      |     |  |
|            |                  |      |     |  |
| ip_address | int(11) unsigned | YES  |     |  |
|            | NULL             |      |     |  |
|            |                  |      |     |  |
| attempted  | datetime         | NO   | MUL |  |
|            | NULL             |      |     |  |
|            |                  |      |     |  |
+------------+------------------+------+-----+--+

Also, for extra clarity this is how the includes/login.inc.php looks:

<?php

//We created this last, but place it before anything else in the document. It simply means that users who are logged in should be able to see this page.
session_start();

//Check if user actually clicked submit button, else throw an error 
if (isset($_POST['submit'])) {

include 'dbh.inc.php';

$uid = mysqli_real_escape_string($conn, $_POST['uid']);
$pwd = mysqli_real_escape_string($conn, $_POST['pwd']);

//Error handlers
//Check if inputs are empty
if (empty($uid) || empty($pwd)) {
    header("Location: ../index.php?login=empty");
    exit();
} else {
    //Check if username exists in database
    //Prepare these fields! (Use prepared statements here)
    $sql = "SELECT * FROM users WHERE user_uid='$uid' OR user_email='$uid'"; 
    //check if username or email is existing
    $result = mysqli_query($conn, $sql);
    $resultCheck = mysqli_num_rows($result);
    if ($resultCheck < 1) {
        header("Location: ../index.php?login=error");
        exit();
    } else {
        if ($row = mysqli_fetch_assoc($result)) { //This takes all the data we requested from the user_uid column in the database, and places it into an array called $row. If we wanted, we could echo out the username like this: echo $row['user_uid'];
            //De-hashing the password
            //Again, use prepared statement
            $hashedPwdCheck = password_verify($pwd, $row['user_pwd']); //Check if password matches, returns true or false
            if ($hashedPwdCheck == false) {
                //wrong password!
                header("Location: ../index.php?pwd=error&uid=".$uid);
                exit();
            } elseif ($hashedPwdCheck == true) {
                //Log in the user here
                $_SESSION['u_id'] = $row['user_id'];
                $_SESSION['u_first'] = $row['user_first'];
                $_SESSION['u_last'] = $row['user_last'];
                $_SESSION['u_email'] = $row['user_email'];
                $_SESSION['u_uid'] = $row['user_uid'];
                header("Location: ../index.php?login=success");

                exit();
            }
        }
    }
}
} else {
    header("Location: ../index.php?login=error");
    exit();
}

I've commented out each step to keep track of what i'm doing...Does the code look 'odd' or 'wrong' in any way? Honestly, I would love to know, what is the proper way to 'block' user attempts during those throttling-delays, please?

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 多址通信方式的抗噪声性能和系统容量对比
    • ¥15 winform的chart曲线生成时有凸起
    • ¥15 msix packaging tool打包问题
    • ¥15 finalshell节点的搭建代码和那个端口代码教程
    • ¥15 用hfss做微带贴片阵列天线的时候分析设置有问题
    • ¥15 Centos / PETSc / PETGEM
    • ¥15 centos7.9 IPv6端口telnet和端口监控问题
    • ¥20 完全没有学习过GAN,看了CSDN的一篇文章,里面有代码但是完全不知道如何操作
    • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
    • ¥20 海浪数据 南海地区海况数据,波浪数据