Inici de sessió

De Wiki personal d'en Guillem Serrat

Inici de sessió

Connexió a la BD i inici de sessió (PHP)

Sempre que es treballa amb sessions, el primer que hem de fer és iniciar-la abans d'escriure el codi HTML

session_start();

A més, hem de requerir el fitxer amb les funcions i una connexió a la BD. En aquest cas, si realitzem una autenticació errònia, haurem d'incrementar el número d'intents de login de la BD i per tant requerim la connexió d'escriptura.

require 'funcions.php';
require './connexioBD/connexioRW.php';

Comprovar si està autenticat

En cas que l'usuari està autenticat, el redirigirem directament a la seva pàgina privada. Per comprovar si l'usuari està autenticat farem servir la funció esta_autenticat.

// Redirigir si ja hi ha una sessió activa
if (esta_autenticat()) {
    header('Location: privada.php');
    exit;
}

Formulari d'inici de sessió

Al formulari es mostra dues entrades de text per introduir el nom d'usuari i la contrasenya. A més, en cas que no s’hagi registrat, tindrà un botó per registrar-se, i si no recorda de les seves credencials, tindrà un botó per poder recuperar la contrasenya.

També es mostra una casella de selecció perque l'aplicatiu el "recordi"

Per últim, també se li dona a l’usuari la oportunitat (sense obligar-lo) a autenticar-se amb 2FA.

El formulari es visualitzaria de la següent forma:

Inici de sessió manual

Obtenció de les dades d'usuari del formulari i la BD

Un cop es respongui el formulari, obtindrem les dades del formulari i realitzarem una consulta per comprovar si el nom d'usuari realment existeix

// Recuperem les dades del formulari
        $nom_usuari = trim($_POST['nom_usuari']);
        $contrasenya = $_POST['contrasenya'];
        $recordar = isset($_POST['recordar']); // Opció per crear o no galetes

        // Recuperem les dades de l'usuari amb el nom d'usuari especificat
        $stmt = $pdo->prepare("SELECT * FROM usuaris WHERE nom_usuari = ?");
        $stmt->execute([$nom_usuari]);
        $usuari = $stmt->fetch();

        // Verifiquem que l'usuari existeixi
        if ($usuari) {

Comprovació d'usuari bloquejat

En cas que existeixi, primerament comprovarem si el compte està bloquejat. Per això comprovarem:

  • Que el nombre d’intents d’inici de sessió que s’ha enregistrat a la BD sigui major o igual a $max_intents, en aquest cas 3 intents
  • Si l’últim intent d’inici de sessió s’ha produit fa menys de $bloqueig_minuts, en aquest cas 2 minuts

Si es cumpleixen els 2 requisits, el compte estarà bloquejat.

// Comprovar si hi ha bloqueig
            if ($usuari['intents_login'] >= $max_intents 
                && strtotime($usuari['ultim_intent']) > strtotime("-$bloqueig_minuts minutes")) {

Per fer el càlcul de les dates fem servir la funció interna de PHP strtotime(), la qual retorna la data introduida en temps UNIX (nº de segons des de 1/1/1970).

strtotime($usuari['ultim_intent']) retorna el número de segons que han passat del 1/1/1970 a la data especificada (o dit d'una altra manera, quans segons han passat des de l’1/1/1970 fins el 10/1/2026 a les 15:00)

strtotime("-$bloqueig_minuts minutes") retorna el número de segons que han passat des de l’1/1/1970 fa $bloqueig_minuts minuts (o dit d'una altra manera, quans segons han passat des de l’1/1/1970 fa 2 minuts)

Així, tenim la mateixa “base temporal” amb la que fer càlculs

En cas que el compte estigui bloquejat, mostrarem a l’usuari el temps que queda per desbloquejar-lo.

Per això:

  1. Calculem el temps (l’hora) UNIX de l’últim intent
    1. Exemple (Exemple: 15:00 emmagatzemat en format UNIX)
  2. Calculem el temps (l’hora) UNIX per que s’acabi el bloqueig
    1. Agafem el temps de l’últim intent i li sumem X minuts en segons, en aquest cas 2 minuts per 60 segons
    2. Exemple: (Exemple: 15:02, emmagatzemat en format UNIX)
  3. Calculem els segons restants
    1. Agafem el temps (hora) perquè s’acabi el bloqueig i restem a l’hora actual
    2. Exemple: 15:02 - 15:01 = 00:01 (60 segons)
  4. Convertim els segons restants a minuts i segons
    1. 80 segons = 1 minut i 20 segons
  5. Mostrem els minuts i segons restants
// Calcular temps restant
                $ultim_intent_ts = strtotime($usuari['ultim_intent']); //Calculem el temps (l’hora) UNIX de l’últim intent 
                $final_bloqueig_ts = $ultim_intent_ts + ($bloqueig_minuts * 60); //Calculem el temps (l’hora) UNIX per que s’acabi el bloqueig
                $segons_restants = $final_bloqueig_ts - time(); // Calculem els segons restants. Agafem el temps (hora) perquè s’acabi el bloqueig i restem a l’hora actual

                $minuts_restants = floor($segons_restants / 60); // Convertim els segons restants a minuts
                $segons_restants = $segons_restants % 60; // Recuperem els segons restants després de calcular les hores

                $error = "Compte bloquejat temporalment. Torna-ho a provar en {$minuts_restants} minuts i {$segons_restants} segons.";

Per tant el fluxe del codi seria el següent:

  1. L’usuari s’equivoca 3 vegades
  2. Intenta accedir una 4ta, no pot (3 intents_login, 3 segons últim intent)
  3. Espera 2 minuts (3 intents_login, 2 minuts últim intent, pot accedir)
  4. Es torna a equivocar una 5na vegada  (3 intents_login, 3 segons últim intent)
  5. Torna a esperar 2 minuts
  6. Accedeix correctament

Comprovació de contrasenya

Un cop verificat que l'usuari no està bloquejat, comprovarem que la contrasenya sigui correcta amb la funció verificar_contrasenya.

 if (verificar_contrasenya($contrasenya, $usuari['contrasenya'])) {

En cas que la contrasenya sigui errònia, augmentarem en 1 els número d'intents d'autenticació, registrarem una acció a la taula activitat de nom "error-login" i registrarem un missatge d'error

// Si la contrasenya és incorrecte, sumem un intent de login

                   $stmt = $pdo->prepare("UPDATE usuaris SET intents_login = intents_login + 1, ultim_intent = NOW() WHERE id = ?");
                   $stmt->execute([$usuari['id']]);
                   // Registrar intent fallit
                   registrar_activitat($pdo, $usuari['id'], 'error-login');
                   $error = "Usuari o contrasenya incorrecte";

Inici de sessió

Si la contrasenya és correcte, actualitzarem el nombre d’intents de login a 0

// Login correcte, resetajer intents
                    $stmt = $pdo->prepare("UPDATE usuaris SET intents_login = 0, ultim_intent = NULL WHERE id = ?");
                    $stmt->execute([$usuari['id']]);

Seguidament, regenerarem la ID de sessió per evitar atacs de sessió fixada

session_regenerate_id(true); // Protecció session fixation

Per acabar creant un vector de nom “usuari” dins de la sessió amb els següents vectors:

  • ID de l’usuari dins la BD
  • Nom d’usuari
  • Nom complet
  • Rol, per definir si és administrador o no i quin tipus
  • Autenticat, per definir que està autenticat
$_SESSION['usuari'] = [ // Iniciem sessió a l'usuari indicant
                        'id' => $usuari['id'], // El seu ID a la BD
                        'nom_usuari' => $usuari['nom_usuari'], // El nom d'usuari
                        'nom_complet' => $usuari['nom_complet'], // El nom complet
                        'rol'        => $usuari['rol'], // El seu rol (usuari, admin)
                        'autenticat' => true // Definim que està autenticat
                    ];

Un cop autenticat, registrarem una acció a la taula activitat de nom "login" i redirigirem l'usuari a la seva pàgina privada

// Registrem l'acció "login"

                   registrar_activitat($pdo, $usuari['id'], 'login');
                   header('Location: privada.php');
                   exit;

"Recordar-me"

En cas que l'usuari hagi decidit que l'aplicació el recordi, generarem un token únic que ens servirà per identificar la seva identitat posteriorment en l'inici de sessió amb cookies

// Generar un token únic i desar-lo a la BD
$token = bin2hex(random_bytes(16));

Aquest token, juntament amb la ID de l'usuari les guardarem en galetes

setcookie('recordar_id', $usuari['id'], time() + 30*24*60*60, '/'); // Recordem la ID de l'usuari setcookie('recordar_token', $token, time() + 30*24*60*60, '/'); // Recordem el token desat a la BD

I per últim, desarem el token únic dins la BD en el registre de l'usuari

$stmt = $pdo->prepare("UPDATE usuaris SET token_recordar = ? WHERE id = ?"); $stmt->execute([$token, $usuari['id']]);

Inici de sessió mitjançant cookies