如果检测到蛮力尝试,PHP 使用 reCAPTCHA

Posted

技术标签:

【中文标题】如果检测到蛮力尝试,PHP 使用 reCAPTCHA【英文标题】:PHP Use reCAPTCHA if brute force attempt is detected 【发布时间】:2014-03-28 08:46:36 【问题描述】:

我正在使用蛮力检查器构建登录脚本,当触发时,它会显示 reCAPTCHA。我遇到的问题是,当输入正确的用户名/密码/验证码响应时,登录脚本会运行,但直到大部分页面内容已加载(这发生在提交表单后)。结果是我必须按 F5 来刷新页面并重新提交表单数据,以便在页面开始加载时会话处于活动状态。

现在,我遇到的问题是,一旦提交表单(当它需要验证码时),直到 index.php 到达会话才会开始

    else 
        $captchaResponse = 1;
        $auth = Auth::verifyPass($userName,$password,$captchaResponse);
    

我不知道如何重新组织它,以便在此之前开始会话。有什么想法吗?

    第一部分是 index.php 页面,其中包含在检测到暴力尝试时触发的代码。这部分代码以条件 if($auth === "bruteForce") 开头 此代码显示 reCAPTCHA 并应该将用户名、密码和 reCAPTCHA 响应代码(0-错误响应,1-正确响应)提交回登录功能。

    <?php
        include('includes/header.php');
        spl_autoload_register(function ($class)
           include 'includes/class.' . $class . '.php'; 
        );
        if(null !==(filter_input(INPUT_POST,'userName')))$userName = filter_input(INPUT_POST,'userName');
        if(null !==(filter_input(INPUT_POST,'password')))$password = filter_input(INPUT_POST,'password');
    
        if(isset($userName)&& isset($password))
            $auth = Auth::verifyPass($userName,$password);
        
    
        if(isset($_GET['logout']) && $_GET['logout'] == true)
            session_start();
            session_destroy();
            setcookie ("PHPSESSID", "", time() - 3600, "/");
            header("Location: index.php");
        
        if(Auth::checkLoggedIn() === true)
            if(session_id() !== '')echo 'Session ID is not blank<br />';
            echo '<a href="index.php?logout=true">Logout</a><br />';
            echo 'Welcome! This is protected content!' . "<br />";
         
        if(!Auth::checkLoggedIn()) :
    ?>
    <h1>Sign In</h1>
    <?php if(isset($userName) && isset($password))if($auth === "invalidPassword")echo '<span class="error">Invalid username or password</span>'; ?>
            <form name="login" method="post" action="index.php" id="loginForm">
                <ul>
                    <li>
                        <input placeholder="Username" type="text" name="userName" id="userName" class="login" />
                    </li>
                    <li>
                        <input placeholder="Password" type="password" name="password" id="password" class="login" />
                    </li>
                    <?php
                        if(isset($userName) && isset($password))
                            echo $auth . "<br />";
                            if($auth === "bruteForce")
                                    echo $auth;
                                    require_once('includes/recaptchalib.php');
    
                                    // Get a key from https://www.google.com/recaptcha/admin/create
                                    $publickey = "xxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
                                    $privatekey = "xxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
                                    $resp = null;
                                    $error = null;
                                    if(isset($_POST["recaptcha_response_field"]))
                                            $resp = recaptcha_check_answer ($privatekey,
                                                                            $_SERVER["REMOTE_ADDR"],
                                                                            $_POST["recaptcha_challenge_field"],
                                                                            $_POST["recaptcha_response_field"]);
    
                                            if ($resp->is_valid) 
                                                Auth::checkLoggedIn();
                                                    $auth = Auth::verifyPass($userName,$password,1);
                                             else 
                                                    $auth = Auth::verifyPass($userName,$password,0);
                                                    //$captchaResponse = 2;
                                            
                                    
                                    echo recaptcha_get_html($publickey, $error);
                                    if($auth === "invalidCaptcha")
                                        echo "Invalid Captcha Response. Please try again.";
                                    
                        
                        
                        if(isset($auth))echo $auth;
                    ?>
                    <div class="clearAll">&nbsp;</div>
                    <li id="submit">
                      <input type="submit" value="Login" id="loginBtn" class="login" />
                    </li>
                    <li id="reset">
                      <input type="reset" value="Reset" id="resetBtn" class="login" />
                    </li>
                </ul>
            </form>
        <div class="clearAll">&nbsp;</div>
        <h1>New User?</h1>
        <p><a href="register.php">Sign Up!</a></p>
        <?php endif; ?>
        <div class="clearAll">&nbsp;</div>
        <?php include('includes/footer.php'); ?>
       </body>
    </html> 
    

    这是登录功能

    public static function verifyPass($username,$password,$captchaResponse = 3)
        $authenticatedUser = FALSE;
            $bruteTest = self::_bruteTest($username);
            if($bruteTest === TRUE && $captchaResponse === 3)
    
            $status = "bruteForce";
            return $status;     
             else if($bruteTest === TRUE  && $captchaResponse === 0)
                //The brute force check was positive and the captcha response failed
                //Don't even try to log in because the captcha failed.
                $status = "invalidCaptcha";
                return $status;
             else if ($bruteTest === TRUE && $captchaResponse === 1)
                //The brute force check was positive and the captcha response was successful
                //Try to log in now.
                $continueLogin = TRUE;
             else if($bruteTest === FALSE)
                //The bruteTest was negative, proceed with login.
                $continueLogin = TRUE;
            
            if($continueLogin === TRUE)
           try
               $connection = Database::getDbConnection();
               if($connection)
                   $query = "SELECT usr_name, usr_pass, usr_salt, uid, email_pri FROM users WHERE usr_name=? LIMIT 1";
                   $stmt = $connection->prepare($query);
                   $stmt->execute(array($username));
                   $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
                   if($stmt->rowCount() === 0)$authenticatedUser = FALSE; //Username was not found  We are not going to say which was incorrect, only that the "username or password was incorrect"
                   if($results)
                   $resultsArray = $results[0];
                   $connection = null;
                   echo "<br />";
                   $dbUserName = $resultsArray['usr_name'];
                   $dbPass = $resultsArray['usr_pass'];
                   $dbSalt = $resultsArray['usr_salt'];
                   $dbUid = $resultsArray['uid'];
                   $dbEmail = $resultsArray['email_pri'];
                   $passHash = hash('sha512',$password);
                   $passToCheck = hash('sha512',$dbSalt.$passHash);
                   if($passToCheck != $dbPass)
                        $authenticatedUser = FALSE; //Password did not match. We are not going to say which was incorrect, only that the "username or password was incorrect"
                    else if ($passToCheck === $dbPass && $username === $dbUserName)
                        $authenticatedUser = TRUE;
                   
                
               else if(!$results)$authenticatedUser = FALSE;
            catch (PDOException $e) 
               echo "Error: " . $e->getMessage() . "<br />";
               die();
           
    
           try
               if($authenticatedUser === FALSE)
                    //Log the failed attempt into the database
                    $remoteIp = $_SERVER['REMOTE_ADDR'];
                    try 
                        $connection = Database::getDbConnection();
                        if($connection)
                            $query = "INSERT INTO `login_attempts`(`usr_name`, `usr_ip`) VALUES (:usr_name,INET_ATON(:usr_ip))";
                            $stmt = $connection->prepare($query);
                            $stmt->execute(array(':usr_name' => $username, ':usr_ip' => $remoteIp));
                        
                        $connection = null;
                     catch (PDOException $e)
                        echo "Error: " . $e->getMessage() . "<br />";
                        die();
                      
                    $status = "invalidPassword";
                    return $status;
                    exit();
                else if($authenticatedUser === TRUE)
                    //Clear login attempts from the database
                    self::_clearAttempts($username);
                    //Start the session (if not already started somehow. session and cookie expiration need to be adjusted so that the session does not persist after browser close)
                    if(!isset($_SESSION))
                        session_start();
                     
                    //Set the session variables
                    $_SESSION['userIp'] = $_SERVER['REMOTE_ADDR'];
                    $_SESSION['userName'] = $dbUserName;
                    $_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
                    $session_name = 'sec_session_id';
                    $httponly = TRUE;
                    $cookieParams = session_get_cookie_params();
                    session_set_cookie_params($cookieParams["lifetime"],
                        $cookieParams["path"], 
                        $cookieParams["domain"],
                        $httponly);
                    session_name($session_name);
                
    
            catch (PDOException $e) 
               echo "Error: " . $e->getMessage() . "<br />";
               die();
           
          //End $continueLogin statement below 
        
    
    

【问题讨论】:

+1 表示这种长且风格不错的书面作品,但您的代码应该被格式化。 感谢您的输入,对于缺少格式,我深表歉意。我仍然习惯于在 Stack Overflow 上发布代码。我已经编辑了我的帖子以包含正确的格式。 请注意,验证码(包括 reCAPTCHA)不是可靠的防御措施。期待 CAPTCHA 的机器人几乎不会被它们拖慢。不过,总比没有好。您也可以考虑在每次连续登录失败时等待越来越长的时间。 感谢您的建议。我的目标是最初让 reCAPTCHA 与这个登录系统一起工作。一旦它起作用,我计划在最后阶段使用 reCAPTCHA 逐步限制登录尝试。我只需要先确定 reCAPTCHA 的实施。我对为什么登录页面需要刷新以重新提交数据以显示受保护的内容感到更加困惑。 【参考方案1】:

如上所述:页面顶部的 session_start(或者甚至将其配置为自动添加到每个 php 脚本,在 PHP ini 中设置 session_auto_start=1)将导致会话数据在脚本启动时加载.

然后,您可以在您测试空用户名和密码的同一位置进行测试,例如 $session['lastPasswordWasInvalid'] 之类的变量,如果已设置,请检查验证码是否为空。

【讨论】:

【参考方案2】:

移动这个:

if(!isset($_SESSION))
    session_start();

到您的脚本顶部并尝试一下。

或者只是:

session_start();

【讨论】:

以上是关于如果检测到蛮力尝试,PHP 使用 reCAPTCHA的主要内容,如果未能解决你的问题,请参考以下文章

人脸检测 OpenCV + Qt + cvMat

如何跟踪以蛮力方法尝试了哪些组合?

蛮力模式搜索java

骑士之旅蛮力

如何在 Java 中正确处理蛮力登录尝试? [关闭]

使用 php 进行可靠的用户浏览器检测