HMAC: Guida completa all’autenticazione basata su hash con chiave

Pre

L’HMAC, o HMAC, è una tecnica di sicurezza fondamentale per proteggere l’integrità e l’autenticità dei messaggi in ambienti digitali. In questa guida esploreremo cos’è l’HMAC, come funziona, quali sono le migliori pratiche per implementarlo e come integrarlo in diverse tecnologie, dalle API REST ai linguaggi di programmazione più comuni. Se vuoi capire come proteggere le comunicazioni tra client e server con una firma basata su hash con chiave, questa guida è pensata per te.

Che cosa è HMAC e perché è importante

HMAC, noto come HMAC, è una costruzione matematica che combina una chiave segreta con una funzione hash per generare una firma. Questa firma serve a garantire due proprietà fondamentali: l’integrità dei dati (distribuzione dei contenuti non è stata alterata) e l’autenticità (chi ha creato la firma possiede la chiave segreta). A differenza di una semplice funzione hash, che è vulnerabile a manomissioni senza chiave, l’HMAC richiede la chiave per generare o verificare la firma.

Nella pratica moderna, l’HMAC è ampiamente utilizzato in protocolli di autenticazione API, notifiche webhook, messaggistica sicura e in molti sistemi di autenticazione che demandano fiducia a una chiave segreta condivisa. È compatibile con diverse funzioni hash, tra cui SHA-256 e SHA-3, offrendo un equilibrio tra velocità e sicurezza. L’uso di HMAC riduce il rischio di attacchi come collisioni of hash e attacchi di tipo replay, a condizione che la chiave sia gestita con attenzione e che i messaggi siano protetti in modo appropriato.

Come funziona HMAC

Il meccanismo di HMAC si basa su una chiave segreta K e su una funzione hash H. In breve, HMAC calcola:

HMAC(K, m) = H((K ⊕ opad) ∥ H((K ⊕ ipad) ∥ m))

dove:

  • K è la chiave segreta, che deve avere una lunghezza adeguata rispetto all’output della funzione hash.
  • ipad è una costante di 0x36 ripetuta per la lunghezza della chiave o del blocco.
  • opad è una costante di 0x5c ripetuta per la lunghezza del blocco.
  • H è la funzione hash scelta (ad es. SHA-256, SHA-3, ecc.).
  • m è il messaggio da firmare o verificare.

Questo schema garantisce che la firma dipenda sia dalla chiave sia dal contenuto del messaggio. La chiave segreta non deve essere condivisa o esposta in chiaro; deve rimanere nota solo alle parti legittimate.

Perché funziona così bene

  • La combinazione tra chiave e messaggio crea una dipendenza forte tra i due elementi: anche una minima modifica del messaggio genera una firma completamente diversa.
  • La presenza di ipad e opad introduce due livelli di ponderazione, rendendo difficile invertire la firma o dedurre la chiave segreta a partire dalla firma stessa.
  • La scelta della funzione hash influisce sulla robustezza. Le librerie moderne supportano SHA-256 o SHA-3 come standard, offrendo contro vulnerabilità note.

HMAC vs MAC: cosa differenzia l’HMAC

Un Message Authentication Code (MAC) è un termine generico per una firma basata su chiave. L’HMAC è una specifica implementazione di MAC che utilizza una funzione hash in modo particolare per assicurare integrità e autenticità. Rispetto ad altre costruzioni MAC, l’HMAC presenta alcune proprietà chiave:

  • Resistenza a collisioni e vulnerabilità di padding
  • Procedura standardizzata per gestire chiavi di lunghezza variabile
  • Compatibilità con una varietà di funzioni hash moderne

Quando si progetta un sistema, è consigliabile privilegiare l’HMAC per i casi d’uso tipici di autenticazione e verifica di integrità, piuttosto che introdurre MAC meno diffusi che potrebbero avere vulnerabilità meno ben comprese.

Componenti chiave di HMAC: chiave, messaggio e firma

Nell’implementazione di HMAC è essenziale considerare alcuni componenti:

La chiave segreta (K)

La chiave deve rimanere segreta tra le parti che autenticano i messaggi. Se la chiave viene esposta, chiunque può generare firme valide. La lunghezza consigliata di K dipende dall’output della funzione hash scelta: per SHA-256, una chiave di almeno 256 bit è comune, ma HMAC è sicuro anche con chiavi più lunghe o leggermente più corte, a seconda delle policy di sicurezza.

Il messaggio (m)

Il contenuto da firmare: se questo cambia, la firma cambia in modo deterministico. Per l’integrità, è importante che i messaggi non vengano alterati durante la trasmissione, e che l’HMAC sia verificato dal destinatario.

La funzione hash H

La funzione hash scelta (ad es. SHA-256) determina l’output di firma. L’HMAC è compatibile con diverse funzioni hash, ma è buona pratica utilizzare una funzione robusta e ben auditable per ridurre il rischio di vulnerabilità.

Esempi pratici di utilizzo di HMAC

Vediamo alcuni scenari concreti in cui l’HMAC può essere impiegato per proteggere l’integrità e l’autenticità dei messaggi:

In API REST e webhook

In un’API REST, l’HMAC può essere usato per firmare le richieste in modo che il server host possa verificare che la richiesta provenga da una fonte attendibile e che il contenuto non sia stato manomesso durante la trasmissione. Un comune schema è il seguente:

  • Il client crea un messaggio o una combinazione di header e payload da inviare al server.
  • Utilizza una chiave condivisa K per calcolare l’HMAC del messaggio.
  • Invia l’HMAC insieme al messaggio, spesso in un header come “X-Signature” o “Authorization”.
  • Il server, conoscendo la chiave K, ricrea l’HMAC sul messaggio ricevuto e lo confronta con quello inviato. Se coincidono, la richiesta è considerata autentica.

Questo approccio evita la necessità di cifrare l’intero contenuto, concentrando la protezione sull’integrità e sull’autenticità delle informazioni trasmesse.

Notifiche e sistemi di messaggistica

In sistemi di notifica o when messaging, l’HMAC garantisce che i messaggi di notifica non vengano alterati da terze parti. L’implementazione tipica prevede una firma HMAC sui payload, verificata dal destinatario prima di elaborare il contenuto.

Implementazioni comuni di HMAC nei linguaggi di programmazione

Di seguito trovi esempi concreti di come si implementa HMAC in alcuni linguaggi di programmazione popolari. Le implementazioni illustrate usano funzionalità standard o librerie affidabili, per assicurare compatibilità e sicurezza.

HMAC in Python

In Python, la libreria standard hashlib e hmac permette di generare e verificare facilmente l’HMAC:

import hmac
import hashlib

def sign_message(key: bytes, message: bytes) -> bytes:
    return hmac.new(key, message, hashlib.sha256).digest()

def verify_message(key: bytes, message: bytes, signature: bytes) -> bool:
    expected = hmac.new(key, message, hashlib.sha256).digest()
    return hmac.compare_digest(expected, signature)

# Esempio di utilizzo
key = b'secret-key-123'
payload = b'{"user_id":42,"action":"login"}'
signature = sign_message(key, payload)

assert verify_message(key, payload, signature)  # True

In questo esempio, hmac viene usato per generare la firma, mentre hashlib.sha256 definisce la funzione di hash. Per la verifica, hmac.compare_digest evita vulnerabilità di timing attack durante la comparazione delle firme.

HMAC in JavaScript (Node.js)

Node.js offre una API nativa per HMAC tramite il modulo crypto. Ecco un esempio:

const crypto = require('crypto');

function signMessage(key, message) {
  return crypto.createHmac('sha256', key)
               .update(message)
               .digest();
}

function verifyMessage(key, message, signature) {
  const expected = signMessage(key, message);
  // Utilizza una comparazione sicura
  return crypto.timingSafeEqual(expected, signature);
}

const key = Buffer.from('secret-key-123');
const payload = Buffer.from('{"user_id":42,"action":"login"}');
const signature = signMessage(key, payload);

console.log(verifyMessage(key, payload, signature)); // true

HMAC in Java

In Java si utilizza la classe Mac del pacchetto javax.crypto:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class HmacExample {
  public static byte[] sign(byte[] key, byte[] message) throws Exception {
    SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA256");
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(secretKey);
    return mac.doFinal(message);
  }

  public static boolean verify(byte[] key, byte[] message, byte[] signature) throws Exception {
    byte[] expected = sign(key, message);
    if (expected.length != signature.length) return false;
    // Timing-attack resistant comparison
    int result = 0;
    for (int i = 0; i < expected.length; i++) {
      result |= expected[i] ^ signature[i];
    }
    return result == 0;
  }
}

HMAC in Go

In Go la libreria standard crypto/hmac insieme a crypto/sha256 offre una soluzione semplice e affidabile:

package main

import (
  "crypto/hmac"
  "crypto/sha256"
  "fmt"
)

func signMessage(key, message []byte) []byte {
  mac := hmac.New(sha256.New, key)
  mac.Write(message)
  return mac.Sum(nil)
}

func verifyMessage(key, message, signature []byte) bool {
  expected := signMessage(key, message)
  // usa una comparazione sicura
  return hmac.Equal(expected, signature)
}

func main() {
  key := []byte("secret-key-123")
  payload := []byte(`{"user_id":42,"action":"login"}`)
  sig := signMessage(key, payload)
  fmt.Println(verifyMessage(key, payload, sig)) // true
}

Buone pratiche di sicurezza con HMAC

Per ottenere la massima efficacia dall’utilizzo di HMAC, segui alcune buone pratiche:

  • Gestione della chiave: conserva la chiave segreta in un luogo sicuro, usa gestione delle chiavi e rotazione periodica delle chiavi quando possibile.
  • Conservazione dei messaggi: se il payload è grande, calcola l’HMAC sul messaggio in streaming evitando di memorizzare copie non necessarie.
  • Scelta della funzione hash: preferisci SHA-256 o SHA-3 per ridurre la probabilità di attacchi di tipo collisione. Evita hash meno resistenti.
  • Protezione contro replay: incorpora nonce, timestamp o counter nel messaggio prima di firmarlo, per evitare attacchi di ripetizione.
  • Verifica della firma: usa metodi di confronto sicuri (timing-safe) per evitare vulnerabilità di timing attack.

Errori comuni da evitare con HMAC

  • Riutilizzare la stessa chiave senza rotazione periodica: pianifica una policy di gestione delle chiavi.
  • Inviare la firma in chiaro senza protezione: usa canali sicuri (HTTPS) e verifica la firma sul destinatario.
  • Trascurare l’inclusione di una informazione temporale: l’assenza di nonce o timestamp facilita gli attacchi di replay.
  • Scelta errata della funzione hash: una funzione debole può creare vulnerabilità nel lungo periodo.
  • Trascurare la verifica lato server: non fidarti di dati firmati solo dal client; esegui sempre una verifica robusta.

Caso pratico: integrazione HMAC in API REST

Entrare in un mondo reale con HMAC significa definire un contratto tra client e server:

  • Il client firma la richiesta (header, corpo) utilizzando la chiave condivisa e trasmette la firma insieme al messaggio.
  • Il server ricrea la firma lato server e la confronta con quella fornita dal client.
  • Se la firma è valida, la richiesta viene accettata; altrimenti viene respinta con un codice di errore adeguato.

Un approccio strutturato prevede di includere elementi come timestamp e nonce per mitigare replay attack. Ecco un flusso semplificato:

  1. Il client aggiunge a un header o al corpo un campo timestamp e un campo nonce.
  2. Il client genera l’HMAC sul contenuto combinato di timestamp, nonce e messaggio effettivo.
  3. Il client invia messaggio e firma; il server verifica che il timestamp sia recente e che il nonce non sia stato utilizzato prima.

HMAC nelle aziende moderne e conformità

In contesti aziendali, l’HMAC è spesso parte integrante di API sicure, integrazione tra servizi e sistemi di notifica. La robustezza della firma basata su hash con chiave lo rende una soluzione elegante per garantire autenticità e integrità in scenari complessi, in cui si richiede la verifica di messaggi tra servizi interni, partnership esterne o sistemi di microservizi. L’implementazione di HMAC, se accompagnata da una gestione delle chiavi rigorosa, può contribuire significativamente al risk management legato all’integrità dei dati e all’autenticità delle richieste in un’architettura distribuita.

FAQ su HMAC

Ecco alcune risposte rapide alle domande frequenti su HMAC:

  • Qual è la differenza tra HMAC e una firma digitale basata su chiave privata?
  • La differenza principale è nel modello di accesso: le firme digitali tradizionali usano una chiave privata e una chiave pubblica per la verifica, mentre l’HMAC si basa su una chiave segreta condivisa tra le parti. Le firme digitali permettono la verifica pubblica, mentre l’HMAC richiede la condivisione segreta tra mittente e destinatario.

  • Quale funzione hash è consigliata con HMAC?
  • Scegli SHA-256 o SHA-3 per una robusta protezione. SHA-1 è ormai considerato insicuro per nuove implementazioni.

  • Posso utilizzare HMAC per firmare grandi payload?
  • Sì, ma è consigliabile calcolare l’HMAC in streaming o su chunk, evitando di creare grandi copie in memoria. Assicurati che la chiave sia protetta e che la verifica sia affidabile.

  • Come proteggere la chiave segreta?
  • Usa strumenti di gestione delle chiavi, ruotazione regolare, accesso limitato, e memorizzazione sicura (preferibilmente in un keystore sicuro o un modulo di sicurezza hardware quando disponibile).

Conclusioni

L’HMAC è una tecnologia di sicurezza versatile e robusta per garantire integrità e autenticità dei messaggi in contesti digitali. La combinazione tra chiave segreta, una funzione hash affidabile e i pad ipad/opad crea una firma resistente agli attacchi più comuni. Implementandolo con una gestione oculata delle chiavi, con pratiche di sicurezza adeguate e con una verifica accurata, si ottiene un livello di protezione elevato per API, webhook e qualsiasi sistema di comunicazione tra componenti.

Se vuoi approfondire ulteriormente, sperimenta con esempi pratici nei linguaggi che usi quotidianamente, verifica di avere una chiave segreta sicura e collega l’HMAC alle logiche di autenticazione, integrità e controllo degli accessi della tua architettura. L’HMAC resta una delle soluzioni più affidabili e mature per proteggere i messaggi nel mondo digitale di oggi.