Brute Force_impossible
1 <?php 2 3 if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) { 4 // Check Anti-CSRF token 5 checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); 6 7 // Sanitise username input 8 $user = $_POST[ 'username' ]; 9 $user = stripslashes( $user ); 10 $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)) ? "" : "")); 11 12 // Sanitise password input 13 $pass = $_POST[ 'password' ]; 14 $pass = stripslashes( $pass ); 15 $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)) ? "" : "")); 16 $pass = md5( $pass ); 17 18 // Default values 19 $total_failed_login = 3; //对输入次数进行限制 20 $lockout_time = 15; 21 $account_locked = false; 22 23 // Check the database (Check user information) 24 $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' ); 25 /*PDO::prepare 为 PDOStatement::execute() 方法准备要执行的SQL语句并返回一个PDOPDOStatement对象。 26 SQL语句可以包含零个或多个命名(:name)或问号(?)参数标记,参数在SQL执行时会被替换 27 */ 28 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 29 /*bindParam()方法指定绑定的参数提供给execute()的方法。 30 第二个参数即$user存储着将要赋给占位符的值, 31 PDO::PARAM_* 明确的表明了参数的类型。 32 */ 33 $data->execute(); 34 //execute()方法负责执行准备好的查询 35 $row = $data->fetch(); 36 37 // Check to see if the user has been locked out. 38 if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) { 39 //rowCount()返回上一个对应的 PDOStatement 对象执行DELETE、 INSERT、或 UPDATE 语句受影响的行数 40 // User locked out. Note, using this method would allow for user enumeration! 41 //echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>"; 42 43 // Calculate when the user would be allowed to login again 44 $last_login = strtotime( $row[ 'last_login' ] ); 45 $timeout = $last_login + ($lockout_time * 60); 46 $timenow = time(); 47 48 /* 49 print "The last login was: " . date ("h:i:s", $last_login) . "<br />"; 50 print "The timenow is: " . date ("h:i:s", $timenow) . "<br />"; 51 print "The timeout is: " . date ("h:i:s", $timeout) . "<br />"; 52 */ 53 54 // Check to see if enough time has passed, if it hasn't locked the account 55 if( $timenow < $timeout ) { 56 $account_locked = true; 57 // print "The account is locked<br />"; 58 } 59 } 60 61 // Check the database (if username matches the password) 62 $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' ); 63 $data->bindParam( ':user', $user, PDO::PARAM_STR); 64 $data->bindParam( ':password', $pass, PDO::PARAM_STR ); 65 $data->execute(); 66 $row = $data->fetch(); 67 68 // If its a valid login... 69 if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) { 70 // Get users details 71 $avatar = $row[ 'avatar' ]; 72 $failed_login = $row[ 'failed_login' ]; 73 $last_login = $row[ 'last_login' ]; 74 75 // Login successful 76 echo "<p>Welcome to the password protected area <em>{$user}</em></p>"; 77 echo "<img src=\"{$avatar}\" />"; 78 79 // Had the account been locked out since last login? 80 if( $failed_login >= $total_failed_login ) { 81 echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>"; 82 echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>"; 83 } 84 85 // Reset bad login count 86 $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' ); 87 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 88 $data->execute(); 89 } else { 90 // Login failed 91 sleep( rand( 2, 4 ) ); 92 93 // Give the user some feedback 94 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>"; 95 96 // Update bad login count 97 $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' ); 98 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 99 $data->execute(); 100 } 101 102 // Set the last login time 103 $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' ); 104 $data->bindParam( ':user', $user, PDO::PARAM_STR ); 105 $data->execute(); 106 } 107 108 // Generate Anti-CSRF token 109 generateSessionToken(); 110 111 ?>
Impossible级别的代码加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,只有等待一段时间之后,才会再次登录,对此爆破也就无法继续。
同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。
学习PDO的网站:
http://www.runoob.com/php/php-pdo.html
http://www.cnblogs.com/pinocchioatbeijing/archive/2012/03/20/2407869.html