Il file login.php, il quale viene invocato da Controller.php nel caso in cui non sono sate fornite le credenziali di accesso, l'ho modificato nella seguente maniera:
Codice PHP:
<html>
<head>esempio.com - login</head>
<body>
<script Language=Javascript src="sha512.js"></script>
<script Language=Javascript src="forms.js"></script>
<?php
if(isset($_GET['error'])) {
echo 'Errore utente non rigistrato!';
}//if
echo $reslt;
?>
<form id="login" action="" method="POST" name="login_form" accept-charset='UTF-8'>
<fieldset >
<legend>Login</legend>
<label for="username">Username</label>
<input id="username" value="" name="username" type="text" required="required" />
<label for="password">Password</label>
<input id="password" name="password" type="password" required="required" />
<input type="submit" name="Login" value="Submit" onclick="formhash(this.form, this.form.password);" />
<button type="reset" class="grey big"><span>Reset</span></button>
</fieldset>
</form>
</body>
</html>
Ora la username e la password vengono passate con Controller, ma la password non viene tramutata in SHA512 dallo Javascript.
Oltre a ciò c'è un altro problema, ossia, quando la classe Controller riceve la coppia username e password, all'intreno del metodo invoke(), demanda l'operazione di login alla classe Model:
Codice PHP:
$reslt = $this->model->getlogin();
Tale classe ingloba due file:
- model/db_connect.php : contenente i parametri per la connessione al database di MYSQL (la connessione avviene!);
- model/functions.php : insieme di funzioni per la gestione dell'attività di login (non è una classe);
Ora quando getlogin() invoca la funzione login($username, $password, $mysqli) presente in funcions.php sembra che questi non dialoghino.
Codice PHP:
...
public function getlogin(){
sec_session_start(); // Avviamo una sessione PHP
//echo '***DEBUG Model.php $_REQUEST[username]='.$_REQUEST['username'].'
';
//echo '***DEBUG Model.php $_REQUEST[password]='.$_REQUEST['password'].'
';
if(isset($_REQUEST['username'], $_REQUEST['password'])) {
$username = $_REQUEST['username'];
$password = $_REQUEST['password']; // L'hash della password.
echo '***DEBUG Model.php username='.$username.'
';
echo '***DEBUG Model.php password='.$password.'
';
if($mysqli->connect_errno > 0){
echo '***DEBUG Model.php mysqli='.$mysqli>connect_error.'
';
}//if
$login_status = $this->login($username, $password, $mysqli);
echo '***DEBUG Model.php login_status='.$login_status.'
';
if($this->login($username, $password, $mysqli) == true) {
// Login avvenuto con successo
echo '***DEBUG Model.php login avvenuto con successo
';
return true;
} else {
// Utente non registrato
echo '***DEBUG Model.php login NON avvenuto con successo
';
header('Location: ./view/login.php?error=1');
}//if
} else {
// Le variabili non sono state inviate correttamente a questa pagina.
echo false;
}//else
...
Per completezza trascrivo anche il contenuto del file functions.php
Codice PHP:
<?php
/**
*
* Gestisce le ssesioni di login generando un id diverso ad ogni
* reload della pagina
*
* @param
* @return
*
*/
function sec_session_start() {
$session_name = 'sec_session_id'; // Mnemonico della sessione
$secure = false; // Impostare a true se si usa https.
$httponly = true; // Nega l'esecuzione di javascript che tentano di accedere al session id.
ini_set('session.use_only_cookies', 1); // Forza la sessione nel far uso di solo cookie.
$cookieParams = session_get_cookie_params(); // Ottiene i parametri memorizzati nel cookie.
session_set_cookie_params($cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], $secure, $httponly);
session_name($session_name); // Imposta i parametri di sessione.
session_start(); // Avvia una nuova sessione
session_regenerate_id(); // rigenera la sessione, cancellando la precedente.
}//sec_session_start
/**
*
* Gestisce le la fase di login verificando se esistono email e password
*
* @param email text email dell'utente registrato
* @param password text password dell'utente registrato
* @param mysqli object connessione al database
* @return true boolean se esistente
*
*/
function login($email, $password, $mysqli) {
echo '***DEBUG functions.php username='.$email.'
';
// Usiamo i prepared Statements al fine di evitare tentativi di SQL injection.
if ($stmt = $mysqli->prepare("SELECT id, username, password, salt FROM members WHERE email = ? LIMIT 1")) {
$stmt->bind_param('s', $email); // definizione della variabile per la sostituzione del segnaposto con la variabile stringa "$email".
$stmt->execute(); // esecuzione dell’istruzione
$stmt->store_result(); //memorizza il risultato
$stmt->bind_result($user_id, $username, $db_password, $salt); // associa il risultato alle variabili.
$stmt->fetch(); //indicizza i risultati
$password = hash('sha512', $password.$salt); // hash sha512 della concatenzazione della password (fornita in input) e salt (estratto dal database).
if($stmt->num_rows == 1) { // Se l'utente esiste
// Verifichiamo se l'utente è bloccato da più tentativi di accesso non andati a buon fine
if(checkbrute($user_id, $mysqli) == true) {
// L'Account è bloccato
return false;
} else {
if($db_password == $password) { // Confrontiamo se la password fornita in input ($password) è uguale a quella presente nel database ($db_password).
// La Password è corretta!
//Ricaviamo le informazioni per la sessione
$user_browser = $_SERVER['HTTP_USER_AGENT']; // ricaviamo il browser dell'utente.
//La funzione ricerca in $user_id i criteri impostati nell'espressione regolare "/[^0-9]+/". Se riconosce dei testi, li sostituisce con "".
$user_id = preg_replace("/[^0-9]+/", "", $user_id); // prevenzione XSS
$_SESSION['user_id'] = $user_id;
$username = preg_replace("/[^a-zA-Z0-9_\-]+/", "", $username); //prevenzione XSS
$_SESSION['username'] = $username;
$_SESSION['login_string'] = hash('sha512', $password.$user_browser);
// Login avvenuto con successo.
return true;
} else {
// Password non corretta
// Registramo il tentativo di accesso fallito
$now = time();
$mysqli->query("INSERT INTO login_attempts (user_id, time) VALUES ('$user_id', '$now')");
return false;
}//else
}//else
} else {
// L'utente non esiste.
return false;
}//else
}//if
}//login
/**
*
* Gestisce i tentativi di accesso a forza bruta
*
* @param user_id text id dell'utente registrato
* @param mysqli object connessione al database
* @return true boolean se tentativo in corso
*
*/
function checkbrute($user_id, $mysqli) {
// Timestamp dell'ora corrente
$now = time();
// Intervallo di analisi dei tenattivi di accesso (2 ore).
$valid_attempts = $now - (2 * 60 * 60);
if ($stmt = $mysqli->prepare("SELECT time FROM login_attempts WHERE user_id = ? AND time > '$valid_attempts'")) {
$stmt->bind_param('i', $user_id);
// Esecuzione della prepared query.
$stmt->execute();
$stmt->store_result();
// Se ci sono stati più di 5 tentativi falliti
if($stmt->num_rows > 5) {
return true;
} else {
return false;
}//if
}//if
}//checkbrute
/**
*
* Verifica la presenza di multisessioni associate ad un utente
*
* @param mysqli object connessione al database
* @return true boolean se in corso più sessioni
*
*/
function login_check($mysqli) {
// verifica se le variabili di sessione sono attive
if(isset($_SESSION['user_id'], $_SESSION['username'], $_SESSION['login_string'])) {
$user_id = $_SESSION['user_id'];
$login_string = $_SESSION['login_string'];
$username = $_SESSION['username'];
$user_browser = $_SERVER['HTTP_USER_AGENT']; // Browser dell'utente.
if ($stmt = $mysqli->prepare("SELECT password FROM members WHERE id = ? LIMIT 1")) {
$stmt->bind_param('i', $user_id); // Bind del parametro "$user_id".
$stmt->execute(); // Esecuzione della prepared query.
$stmt->store_result();
if($stmt->num_rows == 1) { // Se l'utente esiste
$stmt->bind_result($password); // Ricaviamo i risultati.
$stmt->fetch();
$login_check = hash('sha512', $password.$user_browser);
if($login_check == $login_string) {
// Connesso!
return true;
} else {
// Non connesso!!
return false;
}//else
} else {
// Non esiste
return false;
}//else
} else {
// Preparated query non andata a buon fine
return false;
}//else
} else {
// Non esistono variabili di sessione
return false;
}//else
}//login_check
?>