Odrzuciłbym ten samouczek, bezpieczeństwo logowania nie powinno być lekceważone, nie powinieneś mieć haseł w postaci zwykłego tekstu w bazie danych, powinny one być zahaszowane z solą i oba powinny się zmienić po pomyślnym zalogowaniu.
Oto skrypt bezpiecznego logowania:
Wykorzystuje PDO do połączenia z bazą danych, rzeczywisty formularz logowania wykorzystuje losowe klucze do logowania np. nie nazwa użytkownika/hasło. Hasła są haszowane sha512 x 25 tys. razy i 16-bajtowym kluczem, chroniącym przed brute force. Mam nadzieję, że to pomoże.
<?php
session_start();
/**
* Table
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(45) DEFAULT NULL,
`pass_hash` varchar(255) DEFAULT NULL,
`pass_salt` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ;
*/
//DB Stuff
define('DBHOST','localhost');
define('DBNAME','yourdb');
define('DBUSER','root');
define('DBPASS','');
//End Config:---
//Open a PDO Database connection
try {
$db = new PDO("mysql:host=".DBHOST.";dbname=".DBNAME, DBUSER, DBPASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}catch (Exception $e){
die('Cannot connect to mySQL server.');
}
class Login{
public $db;
public $user;
public $pass;
public $error;
// sha512
public $algo = '$6';
// Cost parameter, 25k iterations
public $cost = '$rounds=25000$';
function __construct(PDO $db){
$this->db = $db;
$this->global_salt = sha1($_SERVER['HTTP_HOST']);
}
/**
* Return a random seed for the mt_rand function
*/
function make_seed(){
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
/**
* Return a random unique salt for new created hash/crypt function salts
*/
function unique_salt(){
$salt = null;
mt_srand($this->make_seed());
for($i=0;$i < mt_rand(1,10);$i++){
$salt = sha1($this->global_salt.$salt.mt_rand().uniqid().microtime(true));
}
return substr($salt,0,16);
}
/**
* Hash a given password and store parts in:
* $this->salt = a unique 16 byte salt
* $this->hash = The full crypted hash sting including algo/cost/salt/crytedpassword
* $this->full_salt = Just algo/cost/salt section, the first 33 bytes
* $this->hashed_password = Just crytedpassword section, proceeding bytes after first 33 bytes
*
*/
function hash($password){
$this->salt = $this->unique_salt();
$this->full_hash = crypt($password, $this->algo.$this->cost.$this->salt);
$this->full_salt = substr($this->full_hash, 0, 33);
$this->hashed_password = substr($this->full_hash, 33);
return $this->full_hash;
}
/**
* Method to validate the given crypto hash against the given password
*/
function check_password($hash, $salt, $password){
$hash = ($this->algo.$this->cost.$salt.'$'.$hash);
if($hash == crypt($password, substr($hash, 0, 33))){
//Regenerate new hash and salt for given password
$this->update_keys();
$this->status = true;
$_SESSION['logged_in']=true;
return true;
}else{
$this->status = false;
return false;
}
}
/**
* Set error
*/
function set_error($type,$value){
$this->error[$type]=$value;
}
/**
* Output error
*/
function error($type){
echo (isset($this->error[$type]))?$this->error[$type]:null;
}
/**
* Logout and regenirate session and redirect to index
*/
static function logout(){
unset($_SESSION['logged_in']);
session_regenerate_id(true);
exit(header('Location: ./index.php'));
}
function anti_brute($intval){
if(!isset($_SESSION['access_time'])){
$_SESSION['access_time']=time();
}else{
$t = time()-$_SESSION['access_time'];
if($t <= $intval){
$this->set_error('global','Time violation');
$_SESSION['access_time']=time();
return true;
}
$_SESSION['access_time']=time();
return false;
}
}
function process_login(){
if($_SERVER['REQUEST_METHOD']=='POST'){
$this->user = (isset($_SESSION['userParam']) && isset($_POST[$_SESSION['userParam']]))?$_POST[$_SESSION['userParam']]:null;
$this->pass = (isset($_SESSION['passParam']) && isset($_POST[$_SESSION['passParam']]))?$_POST[$_SESSION['passParam']]:null;
$this->create = (isset($_SESSION['createParam']) && isset($_POST[$_SESSION['createParam']]))?$_POST[$_SESSION['createParam']]:null;
$cont = true;
if($this->user == null || strlen($this->user) <= 2){$this->set_error('user','Please enter a username!'); $cont=false;}
if($this->pass == null || strlen($this->pass) <= 2){$this->set_error('pass','Please enter a password!'); $cont=false;}
if($cont==true){
//Alls good continue
if($this->create != null && $this->create=='1'){
//Check user for new account
if($this->check_user()==true){$this->set_error('user','Username already taken.');return;}
//Create account
$this->create_account();
}else{
//Stop really fast request 2 seconds
if($this->anti_brute(2)==false){
//Attempt to login
$this->check_login();
}
}
}else{
//Error with form
$this->set_error('global','Please fill in login form!');
}
}
}
function check_user(){
$sql = 'SELECT 1 FROM users WHERE username=:username';
$statement = $this->db->prepare($sql);
$statement->bindParam(':username', $this->user, PDO::PARAM_STR);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
if(!empty($result)){return true;}else{return false;}
}
function check_login(){
$sql = 'SELECT pass_hash, pass_salt FROM users WHERE username=:username';
$statement = $this->db->prepare($sql);
$statement->bindParam(':username', $this->user, PDO::PARAM_STR);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
$this->check_password($result['pass_hash'], $result['pass_salt'], $this->pass);
}
function create_account(){
//Create new account
$this->hash($this->pass);
$sql = 'INSERT into users (username, pass_hash, pass_salt) VALUES (:username, :pass_hash, :pass_salt)';
$statement = $this->db->prepare($sql);
$statement->bindParam(':username', $this->user, PDO::PARAM_STR);
$statement->bindParam(':pass_hash', $this->hashed_password, PDO::PARAM_STR);
$statement->bindParam(':pass_salt', $this->salt, PDO::PARAM_STR);
$statement->execute();
$this->status = true;
$_SESSION['logged_in']=true;
}
function update_keys(){
//Update account password hash & salt
$this->hash($this->pass);
$sql = 'UPDATE users SET pass_hash=:pass_hash, pass_salt=:pass_salt WHERE username=:username';
$statement = $this->db->prepare($sql);
$statement->bindParam(':username', $this->user, PDO::PARAM_STR);
$statement->bindParam(':pass_hash', $this->hashed_password, PDO::PARAM_STR);
$statement->bindParam(':pass_salt', $this->salt, PDO::PARAM_STR);
$statement->execute();
$this->status = true;
$_SESSION['logged_in']=true;
}
}//END Login class
//Logout handler
if(isset($_GET['logout'])){ Login::logout(); }
$login = new Login($db);
//Login handler
$login->process_login();
//Debug
echo '<pre>';
print_r($login);
echo '</pre>';
//Check login status
if(isset($_SESSION['logged_in']) && $_SESSION['logged_in']==true){
//Logged in
echo '<a href="?logout">Logout</a>';
}else{
//Not Logged In
//Show login form & create uniqie parrams for user/pass/create post keys
$_SESSION['userParam'] = sha1(uniqid().microtime(true));
$_SESSION['passParam'] = sha1(uniqid().microtime(true));
$_SESSION['createParam'] = sha1(uniqid().microtime(true));
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Secure Login</title>
</head>
<body>
<h1>Secure Login Example</h1>
<h3>Please login:</h3>
<?php $login->error('global'); ?>
<form method="POST" action="">
<label for="user">Username : </label>
<input type="text" name="<?=$_SESSION['userParam'];?>" size="29"> <?php $login->error('user'); ?>
<br />
<label for="pass">Password : </label>
<input type="text" name="<?=$_SESSION['passParam'];?>" size="29"> <?php $login->error('pass'); ?>
<br />
<input type="submit" value="Login"> and create my account:<input type="checkbox" name="<?=$_SESSION['createParam'];?>" value="1">
</form>
</body>
</html>
<?php } ?>
';//Sprawdź login statusif(isset($_SESSION['logged_in']) &&$_SESSION['logged_in']==true){ //Zalogowano echo 'Wyloguj
';}else{ //Niezalogowany //Pokaż formularz logowania i utwórz unikalne parametry dla użytkownika/pass/utwórz klucze poczty $_SESSION['userParam'] =sha1(uniqid().microtime(true)); $_SESSION['passParam'] =sha1(uniqid().microtime(true)); $_SESSION['createParam'] =sha1(uniqid().microtime(true));?>