Autenticazione con JWT

Questo articolo esplora l’uso di JSON Web Token (JWT) per autenticare servizi RESTful. Tratteremo i principi, i vantaggi e gli svantaggi di JWT, con esempi pratici in PHP.

Prerequisiti

Si richiede familiarità con l’architettura RESTful, e basi di programmazione in PHP.

Introduzione

Il bisogno di limitare l’accesso a risorse specifiche agli utenti autenticati è comune sia per applicazioni web che API. JWT offre una soluzione sicura, stateless e scalabile, rispettando i principi RESTful.

Cos’è un JSON Web Token?

Un JWT è una stringa codificata che contiene tre sezioni principali:

  • Header: Contiene il tipo di token (JWT) e l’algoritmo di firma (ad esempio, HMAC-SHA256).
  • Payload: Include i claims, ovvero informazioni sull’utente come ID, ruoli e scadenza del token.
  • Signature: Una firma generata utilizzando una chiave segreta per garantire integrità e autenticità.

Esempio di JWT


        eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwiaWF0IjoxNjgwODg4MDAsImV4cCI6MTY4MDg5MTYwfQ.s6EoZQc9m3VfRu77E3J9PqjD5Ikg1N8d0Ilx_EBzYYQ
        

Autenticazione Stateless

In ambienti RESTful come micorservizi ed api delle applicazioni SPA, il server non mantiene stato tra richieste successive. JWT permette al client di inviare tutte le informazioni relative all’utente autenticato per ogni richiesta, evitando la gestione di sessioni lato server.

Vantaggi di JWT

  • Scalabilità: Riduce il carico sul server.
  • Efficienza: Le informazioni sono incluse nel token stesso.
  • Sicurezza: La firma garantisce che i dati non siano stati alterati.

Svantaggi di JWT

  • Compromissione della chiave: Se la chiave privata sul server con cui sono firmati i token viene scoperta, l’intero sistema è vulnerabile.
  • Revoca complessa: Non è possibile invalidare i token senza una blacklist.
  • Lunghezza del token: Può aumentare il carico di rete.

Implementazione in PHP

Generazione di un Token


        require 'vendor/autoload.php';

        use Firebase\JWT\JWT;

        $key = "your_secret_key";
        $payload = [
            "iss" => "example.com",
            "aud" => "example.com",
            "iat" => time(),
            "exp" => time() + 3600,
            "user_id" => 12345,
            "roles" => ["ROLE_ADMIN"]
        ];

        $jwt = JWT::encode($payload, $key, 'HS256');
        echo $jwt;
        

Validazione di un Token


        require 'vendor/autoload.php';

        use Firebase\JWT\JWT;
        use Firebase\JWT\Key;

        preg_match('/Bearer\s(\S+)/', $headers['Authorization'], $matches));
        $jwt = $matches[1];
        $key = file_get_contents('privateKey.pem');

        try {
            $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
            print_r($decoded);
        } catch (Exception $e) {
            echo 'Token non valido: ', $e->getMessage();
        }
        
diagramma di sequenza uml jwt autenticazione
diagramma di sequenza uml jwt autenticazione

Riferimenti

Autenticazione Utente con le Sessioni

Introduzione

In un’applicazione web tradizionale, l’autenticazione degli utenti si basa sul meccanismo di sessione tra il server e il client, ovvero uno spazio di memoria a cui accede il server, spazio di memoria associato al client.
Il collegamento logico / il filo conduttore tra la sequenza di transazioni è l “id di sessione” generato dal webserver, memorizzato nei cookie del browser, ed inviato dal client nei cookies ad ogni richiesta.

Questo articolo analizza i passaggi principali di: login, verifica dello stato autenticato e logout. La descrizione è accompagnata da esempi di codice PHP.

Concetti chiave

Autenticazione: è il processo di controllo delle credenziali utente (username/password). L’autenticazione dell’utente ha successo se viene trovata una corrispondenza tra le credenziali inserite in fase di login con i dati salvati in fase di registrazione.

Autorizzazione: è il controllo dei diritti dell’utente di accedere a determinate risorse.

1. Login

Durante la fase di login, l’utente inserisce le proprie credenziali (nome utente e password) in un form HTML. Il server valida queste credenziali e, in caso di successo, avvia una sessione. Ecco i passaggi principali:

  1. Invio delle credenziali: Le credenziali vengono inviate al server tramite una richiesta HTTP POST.
  2. Validazione delle credenziali: Il server verifica le credenziali confrontandole con i dati memorizzati (es. in un database).
  3. Avvio della sessione: In caso di successo, il server avvia una sessione utilizzando session_start() e memorizza informazioni sull’utente autenticato nella superglobale $_SESSION.
<?php
// Pseudocodice per il login
session_start();

// Validazione delle credenziali
$username = $_POST['username'];
$password = $_POST['password'];

if (validate_credentials($username, $password)) {
    $_SESSION['user'] = [
        'id' => get_user_id($username),
        'username' => $username
    ];
    header('Location: dashboard.php');
    exit;
} else {
    echo 'Credenziali non valide.';
}
?>

2. Verifica dello Stato Autenticato

Ad ogni richiesta HTTP, il server verifica se l’utente è autenticato controllando i dati nella sessione. Questo processo garantisce che solo gli utenti autenticati possano accedere a risorse protette.

<?php
// Pseudocodice per proteggere una pagina
session_start();

if (!isset($_SESSION['user'])) {
    // Reindirizza l'utente alla pagina di login
    header('Location: login.php');
    exit;
}

// L'utente è autenticato, procedi con la logica della pagina
echo 'Benvenuto, ' . htmlspecialchars($_SESSION['user']['username']);
?>

Diagramma uml di sequenza
Diagramma uml di sequenza

3. Logout

Il logout termina la sessione dell’utente eliminando i dati memorizzati e distruggendo l’ID di sessione. Questo passaggio previene ulteriori accessi senza una nuova autenticazione.

<?php
// Pseudocodice per il logout
session_start();

// Elimina i dati della sessione
$_SESSION = [];

// Distrugge la sessione
session_destroy();

// Reindirizza alla pagina di login
header('Location: login.php');
exit;
?>

Considerazioni sulla Sicurezza

  • Abilita session.cookie_secure per trasmettere i cookie solo su connessioni HTTPS.
  • Usa session_regenerate_id() dopo il login per prevenire attacchi di session fixation.
  • Configura session.cookie_httponly per evitare accessi JavaScript ai cookie.

Configurazione di LEMP Stack con Docker e Certificati SSL


Questo tutorial ti guida passo dopo passo nella configurazione di uno stack LEMP con Docker e Docker Compose, integrando la protezione SSL tramite Let’s Encrypt.

Prerequisiti

  • Un server Ubuntu con un utente non root abilitato a sudo e un firewall attivo.
  • Docker e Docker Compose installati.
  • Un dominio registrato con i record DNS configurati (A e www).

Fase 1: Configurazione del Server Web

Crea la directory di configurazione del progetto e il file nginx.conf:


mkdir -p webserver_www/nginx-conf
nano webserver_www/nginx-conf/nginx.conf

            

Inserisci il seguente blocco di configurazione:


server {
    listen 80;
    server_name your_domain www.your_domain;
    root /var/www/html;

    location ~ /.well-known/acme-challenge {
        allow all;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass php_fpm:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.ht {
        deny all;
    }
}

            

Nota: Ricordati di sostituire your_domain con il tuo dominio effettivo.

Fase 2: Definizione delle Variabili Ambientali

Crea un file .env per definire variabili sensibili:


nano webserver_www/.env

            

Inserisci i seguenti valori:


MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_php_fpm_user
MYSQL_PASSWORD=your_php_fpm_password
MYSQL_DATABASE=your_database

            

Fase 3: Configurazione di Docker Compose

Crea il file docker-compose.yml per definire i servizi:


version: '3'
services:
  db:
    image: mysql:8.0
    env_file: .env
    volumes:
      - dbdata:/var/lib/mysql
    networks:
      - app-network

  php_fpm:
    image: php:8.2-fpm
    volumes:
      - php_fpm:/var/www/html
    networks:
      - app-network

  nginx:
    image: nginx:1.21-alpine
    ports:
      - "80:80"
    volumes:
      - php_fpm:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
    networks:
      - app-network

  certbot:
    image: certbot/certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - php_fpm:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email you@example.com --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

volumes:
  dbdata:
  php_fpm:
  certbot-etc:

networks:
  app-network:
    driver: bridge

            

Fase 4: Configurazione Certificati SSL

Esegui il comando per ottenere i certificati:


docker-compose up -d certbot

            

Verifica la presenza dei certificati con:


docker-compose exec nginx ls -la /etc/letsencrypt/live

            

Fase 5: Modifica della Configurazione Nginx

Modifica nginx.conf per abilitare HTTPS:


server {
    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
}

            

Fase 6: Rinnovo Automatico dei Certificati

Crea uno script di rinnovo:


#!/bin/bash
docker-compose run certbot renew && docker-compose kill -s SIGHUP nginx

            

Configura un cron job:


crontab -e
# Aggiungi la seguente riga
0 12 * * * /path/to/ssl_renew.sh >> /var/log/cron.log 2>&1