medicus.global · 2026-05-05
"Tutte le informazioni le ho inserite io stessa, e acconsento che i
soccorritori e i medici possano leggerle — per dare loro la migliore
conoscenza preliminare possibile della mia situazione personale,
specialmente in un'emergenza. Il mio amore per la vita è più grande
delle regole del paese sulla protezione dei dati."
Questa è la decisione adulta e volontaria dietro ogni profilo My Medicus creato. Il nostro compito è onorare la scelta della paziente: conservare i dati in modo sicuro, mostrarli solo a chi ha il PIN corretto, lasciare che la paziente veda ogni scansione, e permettere alla paziente di revocare la tessera nel secondo in cui è persa. Il resto di questo documento spiega come.
"Qual è la vostra sicurezza riguardo a My Medicus?"
In un'emergenza, il più grande desiderio della paziente è guarire. È lei che serviamo. La sicurezza è il mezzo, non il fine.
Tre principi prevalgono su tutto:
Abbiamo documentato 19 diversi scenari di attacco contro My Medicus ed eseguito ciascuno contro il sistema di produzione. Il risultato è descritto sotto — senza linguaggio di marketing, senza "standard del settore", senza "best practice". Solo ciò che è stato testato concretamente.
19 scenari di attacco contro due account nel database live — un account demo e un nuovo utente reale. Strumenti: richieste HTTP dirette, esecuzione parallela, introspezione SQL del database di produzione, parsing delle risposte.
Risultato: 14 attacchi hanno incontrato difese che hanno tenuto. 5 vulnerabilità sono state trovate e chiuse lo stesso giorno.
| Attacco | Perché l'attacco è fallito |
|---|---|
| Furto dell'intero database | Allergie, farmaci, codici fiscali, passaporto, EHIC e numeri di assicurazione sono cifrati con AES-256-GCM. La chiave vive in uno store di segreti separato, mai nel database. Se rubi il file DB, ottieni testo illeggibile |
| Forza bruta su PIN a 6 cifre | Ritardi progressivi si attivano al 5° tentativo errato: 5 sec → 30 sec → 60 sec tra ciascuno |
| SQL injection sul form di login | Tutte le query al database usano query parametrizzate — cinque payload di iniezione diversi sono stati respinti |
| Chiamate API dirette senza login | Tutti gli endpoint protetti restituiscono HTTP 401 |
| Indovinare token | I token sono 32 caratteri hex derivati via SHA-256. Spazio di ricerca: 16^32. 50 tentativi casuali → 0 successi |
| Path-traversal a backup o config | /_db_backups/, /.env, /.git, /medicus.db, /wp-config.php restituiscono tutti 404 |
| Enumerazione email via "password dimenticata" | Risposta generica coerente indipendentemente dal fatto che l'email esista — nessuna fuga di informazioni |
| XSS nei campi pubblici | Il server rifiuta input malformati, nessuna riflessione di HTML/JS |
| Furto fisico di portafoglio senza PIN della tessera | Il blocco PIN ferma tutti i tentativi non corretti |
| 10 tentativi paralleli di forza bruta | Il rate-limit si applica per token, non per richiesta |
| Cancellazione del log di audit via endpoint DELETE | Nessun endpoint DELETE esposto |
Indovinare admin-token (admin, 1234, letmein) |
Tutti restituiscono 401 |
| Tentativo di far trapelare segreti al server via payload di errore | Né chiave di cifratura né segreti Stripe appaiono nell'output di errore |
| Abuso cross-origin da un altro sito | Il browser blocca per same-origin policy |
| # | Cosa | Severità | Cosa è stato fatto |
|---|---|---|---|
| F1 | Il login rispondeva 200 ms più lentamente quando l'email era registrata rispetto a quando non lo era. Un attaccante poteva determinare se un account esiste | 🔴 Critico | Il login ora esegue sempre la verifica Argon2id (anche contro email sconosciute, con un hash dummy). Tempo di risposta costante. Messaggio di errore generico |
| F2 | Nessuna intestazione di sicurezza (HSTS, CSP, X-Frame, X-Content-Type, Referrer-Policy, Permissions-Policy) | 🔴 Critico | Tutte e 6 le intestazioni vengono ora aggiunte su tutte le risposte via middleware. CSP blocca script/img/style/frame su sorgenti whitelisted |
| F3 | Una tessera rubata ha QR + PIN stampati direttamente. Il ladro ha accesso fino alla revoca del proprietario | 🟠 Alto | (1) Paziente vede ogni scansione nel proprio dashboard ("Mio registro" — prefisso IP, paese, ora). (2) Il proprietario può revocare la tessera con un clic → tutte le tessere stampate muoiono istantaneamente + nuovo PIN generato |
| F4 | /admin restituiva SPA-HTML a tutti, rivelando l'esistenza del pannello admin |
🟡 Basso | Restituisce ora lo stesso 404 dei percorsi inesistenti per utenti non autenticati |
| F5 | Password dimenticata poteva essere abusata per inondare la casella di un bersaglio (1.000 mail/ora) — e contemporaneamente danneggiare la nostra reputazione di mittente | 🟠 Alto | Se l'utente ha un token reset valido (max 2 ore), viene riutilizzato invece di inviare nuova mail. Più rate-limit per email di 2/min |
| Scelta | Motivazione |
|---|---|
| Nessuna autenticazione nazionale nella vista paramedico | La paziente è incosciente sul campo da golf. Il paramedico non ha autenticazione nazionale, e anche se l'avesse, non abbiamo 90 secondi per farli accedere. PIN-su-tessera è la risposta pratica |
| Non "zero-knowledge" | Il server PUÒ decifrare i campi — è condizione affinché il paramedico possa vedere i dati. Non chiamiamo "zero-knowledge" ciò che non lo è |
| Nessun import automatico di dati | Ogni campo è scelto dall'utente. Nessun auto-import da altre piattaforme sanitarie |
Se un paramedico scansiona legittimamente la tessera con il PIN corretto, può fotografare lo schermo. Non possiamo impedirlo — è il prezzo perché i dati possano essere mostrati in un'emergenza. Difesa:
È lo stesso modello di sicurezza di un braccialetto identificativo medico fisico per diabetici: i dati sono visibili ai soccorritori, accettiamo che anche un borseggiatore possa vederli. La differenza è che il nostro modello dà alla paziente controllo su chi vede cosa e quando — non solo visibilità.
Domande? Scrivi a peter@medicus.global