<?php if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Sanitise username input $user = $_POST[ 'username' ]; $user = stripslashes( $user ); $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Sanitise password input 1 $pass = $_POST[ 'password' ]; 1 $pass = stripslashes( $pass ); 1 $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); 1 $pass = md5( $pass ); 1 // Default values 1 $total_failed_login = 3; 1 $lockout_time = 15; 1 $account_locked = false; 1 // Check the database (Check user information) 2 $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' ); 2 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 2 $data->execute(); 2 $row = $data->fetch(); 2 // Check to see if the user has been locked out. 2 if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) { 2 // User locked out. Note, using this method would allow for user enumeration! 2 //echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>"; 2 // Calculate when the user would be allowed to login again 2 $last_login = strtotime( $row[ 'last_login' ] ); 3 $timeout = $last_login + ($lockout_time * 60); 3 $timenow = time(); 3 /* 3 print "The last login was: " . date ("h:i:s", $last_login) . "<br />"; 3 print "The timenow is: " . date ("h:i:s", $timenow) . "<br />"; 3 print "The timeout is: " . date ("h:i:s", $timeout) . "<br />"; 3 */ 3 // Check to see if enough time has passed, if it hasn't locked the account 3 if( $timenow < $timeout ) { 3 $account_locked = true; 4 // print "The account is locked<br />"; 4 } 4 } 4 // Check the database (if username matches the password) 4 $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' ); 4 $data->bindParam( ':user', $user, PDO::PARAM_STR); 4 $data->bindParam( ':password', $pass, PDO::PARAM_STR ); 4 $data->execute(); 4 $row = $data->fetch(); 4 // If its a valid login... 5 if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) { 5 // Get users details 5 $avatar = $row[ 'avatar' ]; 5 $failed_login = $row[ 'failed_login' ]; 5 $last_login = $row[ 'last_login' ]; 5 // Login successful 5 echo "<p>Welcome to the password protected area <em>{$user}</em></p>"; 5 echo "<img src=\"{$avatar}\" />"; 5 // Had the account been locked out since last login? 5 if( $failed_login >= $total_failed_login ) { 6 echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>"; 6 echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>"; 6 } 6 // Reset bad login count 6 $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' ); 6 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 6 $data->execute(); 6 } else { 6 // Login failed 6 sleep( rand( 2, 4 ) ); 7 // Give the user some feedback 7 echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>"; 7 // Update bad login count 7 $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' ); 7 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 7 $data->execute(); 7 } 7 // Set the last login time 7 $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' ); 7 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 8 $data->execute(); 8 } 8 // Generate Anti-CSRF token 8 generateSessionToken(); 8 ?>
可以看到,impossible级别在 high 的基础上对用户的登录次数有所限制,当用户登录失败达到3次,将会锁住账号15秒,同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这里因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。