Padroneggiare l’Arte della Sicurezza delle Progressive Web App
Le Progressive Web App (PWA) sono diventate lo standard de‑facto per offrire esperienze simili a quelle delle app sul web aperto. I loro punti di forza — capacità offline, notifiche push e installabilità — introducono anche nuove superfici d’attacco che i siti web tradizionali raramente affrontano. Questo articolo approfondisce il ciclo di vita della sicurezza di una PWA, combinando indurimento a livello di rete, protezioni a runtime e migliori pratiche operative. Alla fine avrai una checklist applicabile a qualsiasi progetto, sia esso un semplice lettore di notizie o una complessa piattaforma e‑commerce.
TL;DR: Metti al sicuro la tua PWA con HTTPS, applica una CSP rigorosa, isola i service worker, convalida tutti gli input e adotta una routine di monitoraggio continuo.
1. Livello di Trasporto – Prima Linea di Difesa
1.1 Imporre HTTPS Ovunque
Tutti i browser moderni richiedono HTTPS per la registrazione dei service worker. Tuttavia, alcune risorse (ad es. analytics di terze parti) possono ancora essere caricate via HTTP, generando avvisi di contenuto misto e aprendo la porta ad attacchi man‑in‑the‑middle (MITM).
Passaggi chiave:
- Ottieni un certificato TLS affidabile (Let’s Encrypt, Cloudflare, ecc.).
- Reindirizza tutte le richieste
http://versohttps://tramite la configurazione del server. - Abilita HSTS (HTTP Strict Transport Security) per forzare i browser a usare HTTPS per un periodo predefinito.
# Esempio di snippet NGINX
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Impostazioni TLS ...
}
Nota: HSTS elimina gli attacchi di downgrade, ma richiede una pianificazione attenta perché, una volta impostato, i browser rifiuteranno connessioni HTTP per tutta la durata specificata.
1.2 Pinning del Certificato (Opzionale)
Per le PWA altamente sensibili — banking, cartelle cliniche — considera il certificate pinning tramite la Public Key Pinning Extension for HTTP (HPKP). Sebbene HPKP sia in fase di deprecazione per i rischi di deployment, è possibile ottenere una sicurezza simile con gli header Expect‑CT che impongono la Certificate Transparency.
Expect-CT: max-age=86400, enforce, report-uri="https://example.com/report"
2. Indurimento del Service Worker
I service worker costituiscono il cuore delle capacità offline di una PWA. Operano in un thread di background con privilegi elevati, diventando un bersaglio primario per l’exploit.
2.1 Limitazione dello Scope
Definisci lo scope più piccolo possibile al momento della registrazione del service worker. Questo impedisce al worker di intercettare richieste al di fuori dell’area prevista.
navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
.then(reg => console.log('SW registrato con scope:', reg.scope));
2.2 Verifica dell’Integrità con Subresource Integrity (SRI)
Se carichi lo script del service worker da un CDN, proteggilo con SRI per assicurarti che il codice recuperato corrisponda a un hash previsto.
<script src="https://cdn.example.com/sw.js"
integrity="sha384-abc123..."
crossorigin="anonymous"></script>
2.3 Content Security Policy per i Service Worker
Una CSP rigorosa può impedire al worker di caricare script malevoli o di connettersi a origini non volute.
Content-Security-Policy: script-src 'self' https://trusted.cdn.com;
connect-src 'self' https://api.example.com;
Suggerimento: Usa la direttiva
worker-src(supportata nei browser più recenti) per includere esplicitamente le origini consentite per gli script dei worker.
2.4 Gestione dello Stato e Validazione della Cache
Non fidarti mai ciecamente delle voci della cache. Valida sempre la freschezza delle risposte cache, soprattutto per token di autenticazione o dati personali.
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.pathname.startsWith('/api/')) {
// Bypass della cache per dati dinamici
event.respondWith(fetch(event.request));
return;
}
// Strategia cache‑first predefinita per risorse statiche
event.respondWith(
caches.match(event.request).then(cached => cached || fetch(event.request))
);
});
3. Protezioni a Runtime – CSP, Permessi e Sandbox
3.1 CSP Completa
Una CSP ben costruita riduce il rischio di Cross‑Site Scripting (XSS) e Data Injection. Di seguito un esempio di policy ottimizzata per una tipica PWA:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'sha256-XYZ' https://analytics.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com wss://socket.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
script-srcinclude un hash per gli script inline, evitandounsafe-inline.frame-ancestors 'none'disabilita il clickjacking.connect-srcwhitelist le API e gli endpoint WebSocket.
3.2 Permissions Policy (ex Feature‑Policy)
Limita le potenti funzionalità del browser che le PWA potrebbero ereditare involontariamente.
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()
3.3 Attributo Sandbox per i Contenuti Incorporati
Se la tua PWA incorpora iframe di terze parti, sandboxali per impedire l’esecuzione di script e la navigazione.
<iframe src="https://maps.example.com"
sandbox="allow-scripts allow-same-origin"
loading="lazy"></iframe>
4. Autenticazione & Autorizzazione
4.1 Autenticazione Basata su Token con JWT
Utilizza JSON Web Tokens (JWT) per l’autenticazione senza stato, ma non conservarli in localStorage — usa cookie Secure, HttpOnly per mitigare il furto via XSS.
Set-Cookie: token=eyJhbGciOi...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
4.2 Rotazione del Refresh Token
Implementa refresh token rotanti per limitare i danni in caso di token compromesso.
- Il client invia il refresh token.
- Il server lo valida e rilascia un nuovo access token e un nuovo refresh token.
- Il vecchio refresh token viene invalidato nel database.
4.3 Mitigazione CSRF
Anche con cookie SameSite, utilizza token anti‑CSRF per le richieste POST che modificano lo stato.
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
Sul server, verifica che il token corrisponda alla sessione.
5. Memorizzazione Sicura dei Dati
Le PWA possono archiviare dati tramite IndexedDB, Cache API e Web Storage. Ognuno ha proprie considerazioni di sicurezza.
| Memorizzazione | Idoneità | Consigli di Sicurezza |
|---|---|---|
| Cache API | Risorse statiche, fallback offline | Cache solo risorse immutabili. Usa nomi di cache versionati per eliminare dati obsoleti. |
| IndexedDB | Dati strutturati, preferenze utente | Cifra i campi sensibili lato client con Web Crypto API. |
| LocalStorage / SessionStorage | Dati piccoli, non sensibili | Evita di memorizzare segreti. I dati sono accessibili a qualsiasi script nella pagina. |
5.1 Esempio: Cifrare Dati in IndexedDB
async function encryptAndStore(key, value) {
const cryptoKey = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(value);
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
cryptoKey,
encoded
);
const db = await openDB('secure-store', 1, {
upgrade(db) { db.createObjectStore('secrets'); }
});
await db.put('secrets', { iv, ciphertext }, key);
}
6. Monitoraggio, Audit e Risposta agli Incidenti
La sicurezza non è una configurazione una tantum; è un ciclo continuo.
6.1 Scansioni Automatizzate
- Usa OWASP ZAP o Burp Suite per rilevare XSS, CSRF e intestazioni insicure.
- Integra le verifiche Lighthouse nelle pipeline CI/CD per imporre la conformità PWA.
6.2 Logging a Runtime
Cattura gli errori del service worker e inoltrali a un endpoint sicuro per l’analisi.
self.addEventListener('error', event => {
fetch('https://logs.example.com/collect', {
method: 'POST',
body: JSON.stringify({ message: event.message, stack: event.error.stack })
});
});
6.3 Playbook di Incident Response
- Rilevare: Allarme attivato da traffico anomalo o segnalazioni di violazione CSP.
- Contenere: Revoca dei JWT compromessi, rotazione delle chiavi API.
- Eradicare: Correzione del codice vulnerabile, aggiornamento della cache del service worker.
- Ripristinare: Deploy della nuova versione, monitoraggio per recidiva.
- Post‑mortem: Documentare la causa radice, aggiornare la checklist di sicurezza.
7. Panoramica Visiva – Strati di Sicurezza in una PWA
flowchart TB
subgraph "Livello di Trasporto"
H["\"HTTPS\""]
S["\"HSTS\""]
end
subgraph "Service Worker"
SW["\"Scope Limitation\""]
CS["\"Cache Validation\""]
end
subgraph "Runtime"
CSP["\"Content Security Policy\""]
PP["\"Permissions Policy\""]
SAN["\"Sandboxed iFrames\""]
end
subgraph "Auth & Data"
JWT["\"Secure Cookies\""]
REF["\"Refresh Rotation\""]
ENC["\"IndexedDB Encryption\""]
end
subgraph "Monitoring"
SCAN["\"OWASP ZAP\""]
LOG["\"Runtime Logging\""]
end
H --> SW --> CSP --> JWT --> SCAN
S --> SW
CS --> PP
PP --> ENC
LOG --> SCAN
Il diagramma illustra come ogni controllo di sicurezza si sovrapponga al precedente, formando un’architettura a difesa in profondità.
8. Checklist – Riferimento Rapido per una PWA Sicura
- Servire tutto il contenuto via HTTPS con HSTS attivo.
- Registrare i service worker con lo scope più ristretto possibile.
- Applicare Subresource Integrity per gli script caricati da remoto.
- Applicare una CSP rigorosa, includendo
worker-srce hash per gli script. - Utilizzare Permissions‑Policy per disabilitare le funzionalità del browser non necessarie.
- Conservare i token di autenticazione in cookie Secure, HttpOnly; mai in localStorage.
- Ruotare i refresh token e validare i token anti‑CSRF per le richieste che modificano lo stato.
- Cifrare i dati sensibili in IndexedDB.
- Eseguire scansioni OWASP ZAP automatizzate ad ogni pull request.
- Configurare logging in tempo reale degli errori del service worker.
- Mantenere un playbook di risposta agli incidenti.
9. Domande Frequenti
D1: Le PWA hanno bisogno di un Service Worker per la sicurezza?
No. Misure come HTTPS, CSP e cookie sicuri funzionano indipendentemente. Tuttavia, il Service Worker è dove la maggior parte degli attacchi a runtime si manifestano, quindi richiede un indurimento aggiuntivo.
D2: Posso usare unsafe-inline nella CSP per gli stili?
È preferibile evitarlo. Usa invece hash o nonce per gli stili inline, oppure spostali in file CSS esterni.
D3: Con quale frequenza devo ruotare i certificati TLS?
Let’s Encrypt rilascia certificati da 90 giorni; automatizza il rinnovo. Per certificati auto‑firmati o commerciali, punta a una validità massima di 1 anno.
10. Conclusioni
Le Progressive Web App sfumano il confine tra esperienze native e web, offrendo potenti capacità che ampliano anche la superficie di attacco. Sovrapponendo la sicurezza del trasporto, limitando gli scope dei service worker, imponendo una rigorosa Content Security Policy e adottando pratiche di autenticazione robuste, puoi proteggere la tua PWA senza sacrificare le performance.
Ricorda: la sicurezza è un viaggio, non una destinazione. Mantieni le dipendenze aggiornate, effettua audit regolari del codice e resta informato sulle nuove minacce. Con le pratiche descritte in questo documento, la tua PWA sarà pronta a resistere agli attacchi web più comuni.