counted

← Tutte le guide

Come Counted cifra un registro condiviso quando il link è la chiave

Ogni progetto Counted ha una chiave casuale di 32 byte che vive nel frammento dell’URL, che i browser non inviano mai a un server. Tutto ciò che un utente digita viene cifrato sul dispositivo con XChaCha20-Poly1305 prima di uscire. Gli account sono facoltativi; quando c’è, anche la password non lascia mai il dispositivo. Il client è AGPL e compilato in modo riproducibile, quindi chiunque può verificare che ciò che serve counted.fr corrisponda al codice sorgente.

Aggiornato il 2 novembre 2026

Il link è la chiave

Creando un progetto, sul dispositivo viene generata una chiave casuale di 32 byte. Il link di condivisione la porta, codificata in base64url, dopo il #:

https://counted.fr/projects/<uuid>#<key>

Secondo la specifica HTTP il frammento resta al client: non viene inviato nella richiesta, quindi non compare mai nei log del server né in un’intestazione Referer. All’apertura, il client salva la chiave in locale e toglie il frammento dalla barra degli indirizzi. È tutto il modello di accesso per chi non ha un account: chi ha il link può leggere e modificare il progetto. Trattalo come la chiave di casa.

Cosa viene cifrato, e come

Ogni record viene serializzato in JSON e cifrato come un unico blob con XChaCha20-Poly1305, con un nonce casuale nuovo di 24 byte a ogni chiamata. Il tag Poly1305 di 16 byte fa sì che una chiave sbagliata o un byte alterato facciano fallire subito la decifratura.

RecordCampi cifrati
Progettonome, valuta, descrizione
Partecipantenome
Spesanome, importo, data, tipo, descrizione, categoria, valuta originale e tasso
Riga di pagamentoimporto, pagato o dovuto
Regola ricorrentemodello, calendario, cursore
Accountnome visualizzato, preferenze, dati di pagamento

Perché non AES-256-GCM: WebAssembly non ha AES-NI, quindi ChaCha20 è in software sia più veloce sia a tempo costante, e il nonce di 24 byte rende le collisioni di nonce casuali un non-problema. La crittografia gira in Rust (il crate chacha20poly1305), compilato in WebAssembly per il web e in nativo per le app iOS e Android. Saldi, rimborsi e grafici sono calcolati anch’essi sul dispositivo: il server non ha mai bisogno di un importo.

Account facoltativi, e quanto costa il deposito delle chiavi

Una chiave che vive su un solo dispositivo è a un telefono perso dallo sparire. Per questo, per chi sceglie di creare un account, ogni chiave di progetto viene conservata anche sul server, avvolta sotto una chiave di account casuale, a sua volta avvolta sotto una chiave derivata sul dispositivo con Argon2id (64 MiB, t=3) dalla password e da un salt per account. Un cambio di password riavvolge solo quella chiave di account; un account che non ha mai cambiato password né creato una chiave di recupero usa ancora direttamente la chiave derivata. Quando un admin cambia la chiave di un progetto, quella nuova arriva a ogni membro con un account in una busta ibrida X25519 + ML-KEM-768; gli altri ricevono un nuovo link. Il server conserva quei byte e non può leggerli.

È un compromesso voluto. Senza deposito, chi avesse il database e la password di un utente non otteneva nulla dei suoi progetti. Con il deposito, ottiene ogni progetto di quell’account. L’alternativa era nessun recupero, che per la maggior parte delle persone è peggio. I membri anonimi non hanno deposito: le loro uniche copie sono il link e il loro dispositivo.

La password non lascia mai il dispositivo

L’accesso non invia la password. Il client invia una prova, un secondo Argon2id della password con un salt di accesso separato, e il server conserva solo SHA-256(proof), confrontato in tempo costante. Niente di ciò che il server riceve o conserva permette di derivare la chiave dell’account, e il server non esegue alcuna derivazione di chiavi propria. Il prezzo è un round trip in più per recuperare il salt di accesso.

Significa anche che non possiamo reimpostare una password al posto tuo. Una chiave di recupero da 256 bit, mostrata una sola volta e mai vista dal server (ne conserva solo un hash derivato), apre un secondo involucro della chiave di account. Senza, una reimpostazione via email ha effetto dopo 72 ore, e una password in uso da 72 ore può annullarla; l’account riparte con una nuova chiave, i progetti tornano dai dispositivi e dai link, e la generazione precedente viene conservata 30 giorni, apribile solo con la vecchia password o con la chiave di recupero.

Cosa vede ancora il server

Dire solo “zero knowledge” sarebbe disonesto. Il server vede:

  • Per gli account: l’email, un hash della prova di accesso, i salt di derivazione delle chiavi, quale account è in quale progetto, i legami di amicizia tra account, hash delle email invitate con un segreto del server come chiave (HMAC-SHA256), chiavi pubbliche, se l’account ha una chiave di recupero e da quando, quando è cambiata la password ed eventuali reimpostazioni, chi amministra ogni progetto, ogni cambio di chiave di un progetto, chi l’ha fatto e quale membro è stato tolto, e, se le notifiche sono attive, il token push e la sua lingua.
  • Per tutti: indirizzo IP, user agent e orario in log conservati al massimo 30 giorni, la dimensione e il momento delle scritture cifrate, e conteggi giornalieri su intere tabelle.
  • La forma di ogni registro, solo in numeri: quanti partecipanti e spese, quando ogni spesa è stata aggiunta, modificata o eliminata e, per un account che ha scelto chi è in un progetto, quale partecipante ha scelto. Dalla versione 3.2.5 sono cifrati anche chi partecipa a una spesa, chi l’ha inserita e se il progetto è chiuso. Un progetto salvato l’ultima volta da una versione precedente conserva quei legami sul server finché uno dei suoi membri non lo apre con un’app aggiornata, che li sposta nei dati cifrati; i backup li conservano fino a 30 giorni in più.
  • Un’importazione da Tricount: il browser non può chiamare Tricount direttamente, quindi il server recupera per lui il registro condiviso e lo passa in chiaro. Non viene né salvato né registrato nei log; il dispositivo lo cifra prima di salvarlo in Counted.
  • La lunghezza del testo cifrato: un cifrario a flusso mantiene la lunghezza del testo in chiaro, quindi il server può distinguere una spesa con una descrizione, o una inserita in valuta estera, da una senza. Mai il contenuto.

Non vede mai nomi, importi, date, descrizioni o nomi di progetto. Ogni colonna in chiaro è elencata e giustificata nelle note di progetto, e un controllo di release fallisce se lo schema ne acquisisce una che non lo è.

Puoi verificare il codice

Il client, tutto ciò che vede mai testo in chiaro, è pubblicato con licenza AGPL su github.com/counted-labs/counted. La build è riproducibile: la CI la rifà da zero, confronta lo SHA-256 di ogni file e firma la lista con Sigstore. counted.fr/it/verify mostra le impronte di ciò che viene servito e come ricompilarlo da solo.

Il server è closed source, per proteggere le funzioni a pagamento per le aziende previste lì. Contiene sempre e solo testo cifrato, quindi questa scelta non cambia nulla per la riservatezza di un registro.

Limiti noti

  • La crittografia end-to-end sul web si fida ancora della pagina che carichi. /verify dimostra che una release corrisponde al suo codice sorgente, e un monitor pubblico verifica ogni sei ore, da GitHub, che counted.fr serva una release firmata e non esegua altro codice. Nessuno dei due può dimostrare che la pagina che hai appena caricato sia quella release: una sostituzione servita a un solo utente passerebbe inosservata. Le app iOS e Android ricevono il loro codice all’installazione, quindi una singola richiesta non può sostituirlo.
  • Ancora nessun audit indipendente. Sono stati documentati sette giri di audit interno, con le relative correzioni.
  • Un link trapelato fa trapelare il progetto finché un admin non ne cambia la chiave. Un membro tolto conserva ciò che aveva già letto o salvato, e un progetto creato senza account ha un admin solo se chi l’ha creato accede sul dispositivo che l’ha creato; quelli più vecchi non ne hanno, e per escludere qualcuno bisogna crearne uno nuovo.

Guardalo su un registro vero

Apri la demo con la scheda di rete del tuo browser: ogni scrittura è testo cifrato.

Continua a leggere

Counted lo facciamo noi. Hai notato un errore in questa guida? Scrivici.