Browser, posta elettronica e sincronizzazione cloud · Lezione 2 di 6
Firefox: places.sqlite e dati di sessione
11 min di lettura
Firefox conserva la cronologia nel proprio schema SQLite, nei propri file JSON e in un formato di sessione compresso, con timestamp calcolati a partire da un'epoca diversa da quella di Chromium. Questa lezione illustra dove si trova un profilo Firefox, quali tabelle rispondono alle domande «quali risorse sono state visitate, quali indirizzi sono stati digitati e quali file sono stati scaricati, e quando» e come aprire i file di sessione che registrano le schede aperte e chiuse di recente. La lezione 4.3 tratta la cache e la navigazione privata.
Individuazione del profilo
In Windows, ogni profilo ha due cartelle con lo stesso nome: %APPDATA%\Mozilla\Firefox\Profiles\<random>.<name> (roaming: i database e i file JSON trattati in questa lezione) e %LOCALAPPDATA%\Mozilla\Firefox\Profiles\<random>.<name> (locale: la cache). Il prefisso è composto da otto caratteri casuali; le versioni attuali assegnano al profilo creato per ogni installazione il nome default-release, ad esempio k3x9p2qa.default-release.
Due file in %APPDATA%\Mozilla\Firefox\ indicano quale profilo era in uso. profiles.ini elenca ogni profilo in una sezione [ProfileN] (Name, IsRelative, Path) e, da Firefox 67, include una sezione [Install<hash>] la cui riga Default= indica il profilo utilizzato da un'installazione. installs.ini ripete questa associazione, con una sezione per ogni hash di installazione. Un profilo denominato default con Default=1 è spesso un profilo preesistente quasi vuoto; occorre quindi seguire la voce [Install…] ed esaminare comunque tutte le cartelle dei profili presenti.
In Linux i profili si trovano in ~/.mozilla/firefox/, mentre la cache è in ~/.cache/mozilla/firefox/. Il pacchetto Snap utilizza ~/snap/firefox/common/.mozilla/firefox/ e Flatpak utilizza ~/.var/app/org.mozilla.firefox/.mozilla/firefox/. Da Firefox 147, le nuove installazioni seguono la struttura XDG in ~/.config/mozilla/firefox/. Cercare tutti i file profiles.ini anziché affidarsi a un unico percorso.
La maggior parte dei database di Firefox opera in modalità write-ahead logging. Acquisire places.sqlite insieme a places.sqlite-wal e places.sqlite-shm (e procedere allo stesso modo per cookie, cronologia dei moduli e favicon): le visite recenti potrebbero essere presenti soltanto nel WAL. Calcolare gli hash dell'insieme di file, quindi lavorare su copie, perché l'apertura di un database con SQLite può eseguire un checkpoint del WAL nel file principale e modificarli entrambi.
places.sqlite
moz_places e moz_historyvisits
moz_places contiene una riga per ogni URL: url, title, rev_host (l'host invertito carattere per carattere con un punto finale, per cui transfer.example.com diventa moc.elpmaxe.refsnart.), visit_count, typed (1 se l'URL è stato digitato almeno una volta), frecency (un punteggio di classificazione per i suggerimenti della barra degli indirizzi, non un valore temporale), last_visit_date e guid. moz_historyvisits contiene una riga per ogni visita: place_id (collegato tramite join a moz_places.id), visit_date, visit_type e from_visit (l'id della visita che ha condotto a questa risorsa, 0 se assente). Entrambe le date sono in formato PRTime: microsecondi trascorsi dal 1970-01-01 UTC (lezione 3.1).
| visit_type | Nome | Significato |
|---|---|---|
| 1 | LINK | Collegamento seguito |
| 2 | TYPED | Indirizzo digitato o selezionato nella barra degli indirizzi |
| 3 | BOOKMARK | Risorsa aperta da un segnalibro |
| 4 | EMBED | Contenuto incorporato; le versioni attuali lo mantengono in memoria, quindi è raramente presente su disco |
| 5 | REDIRECT_PERMANENT | Risorsa raggiunta tramite un reindirizzamento permanente, come HTTP 301 |
| 6 | REDIRECT_TEMPORARY | Risorsa raggiunta tramite un reindirizzamento temporaneo, come HTTP 302 |
| 7 | DOWNLOAD | URL recuperato come download |
| 8 | FRAMED_LINK | Collegamento seguito all'interno di un frame |
| 9 | RELOAD | Pagina ricaricata |
Seguendo a ritroso from_visit si ricostruisce una catena di navigazione. Firefox elimina automaticamente la cronologia meno recente, quindi l'assenza di una visita non dimostra che non sia avvenuta.
Segnalibri, input digitato e origini
moz_bookmarks memorizza segnalibri (type 1), cartelle (2) e separatori (3), con fk che punta a moz_places.id, parent, title, e dateAdded e lastModified in formato PRTime. moz_inputhistory registra ciò che l'utente ha digitato nella barra degli indirizzi e quale risultato ha poi scelto: input (il testo digitato), place_id (la pagina selezionata) e use_count, un punteggio che diminuisce nel tempo. Non contiene timestamp, quindi occorre datarlo tramite le visite corrispondenti. moz_origins conserva una riga per ogni coppia schema-host (prefix, host, frecency), a cui fa riferimento moz_places.origin_id.
Download
Da Firefox 26, i download sono registrati in places.sqlite; i profili precedenti utilizzavano un file separato, downloads.sqlite. Un download aggiunge una visita di tipo 7 per l'URL di origine e due annotazioni di pagina in moz_annos, associate al place_id di tale URL e denominate tramite moz_anno_attributes.name:
downloads/destinationFileURI:contentcontiene il percorso di salvataggio sotto forma di URI, ad esempiofile:///C:/Users/jsilva/Downloads/Q1_payroll.zip.downloads/metaData:contentcontiene JSON constate(1 completato, 2 non riuscito, 3 annullato; altri valori indicano download in pausa o bloccati),endTimein millisecondi dal 1970 UTC,fileSize(presente quando il download è riuscito) edeleted.
Le righe di moz_annos contengono anche dateAdded e lastModified in formato PRTime. Cercare riscontri nel file system: la voce $MFT del file e il relativo flusso Zone.Identifier (lezione 1.4).
Altri file del profilo
favicons.sqlite: icone (moz_icons) collegate agli URL delle pagine (moz_pages_w_icons), una traccia di riscontro del caricamento di una pagina o della sua aggiunta ai segnalibri.formhistory.sqlite:moz_formhistorymemorizza i valori digitati nei campi dei moduli web:fieldname,value,timesUsed,firstUsedelastUsed(PRTime). Qui compaiono anche le caselle di ricerca, spesso confieldnamepari asearchbar-history.cookies.sqlite:moz_cookiesconhost,name,value,creationTimeelastAccessed(PRTime) edexpiry. I valori non sono cifrati. Le versioni precedenti memorizzanoexpiryin secondi Unix e quelle attuali in millisecondi, quindi occorre verificare il numero di cifre.permissions.sqlite:moz_permsregistra le autorizzazioni dei siti (origin,typecomedesktop-notificationocamera,permission,modificationTime).logins.json: credenziali salvate.hostname,formSubmitURLe i valori temporali sono leggibili;encryptedUsernameedencryptedPasswordsono cifrati con una chiave conservata inkey4.db(database delle chiavi NSS, in formato SQLite da Firefox 58), protetta dalla password principale, se impostata.extensions.json: componenti aggiuntivi installati conid,version,active,installDateeupdateDate(millisecondi) epath; i pacchetti si trovano inextensions\come file.xpi.addons.jsonmemorizza nella cache i metadati dello store dei componenti aggiuntivi. Cercare componenti aggiuntivi che l'utente non avrebbe installato consapevolmente.
Dati di sessione
La cartella sessionstore-backups contiene lo stato della sessione:
recovery.jsonlz4: la sessione corrente, riscritta mentre Firefox è in esecuzione (ogni 15 secondi per impostazione predefinita).recovery.baklz4: la copia precedente del file di ripristino.previous.jsonlz4: la sessione precedente all'ultimo riavvio.upgrade.jsonlz4-<buildID>: una copia creata all'aggiornamento di Firefox, denominata con l'ID della build a 14 cifre.- Alla chiusura regolare, Firefox scrive anche
sessionstore.jsonlz4nella radice del profilo.
I file utilizzano il contenitore LZ4 specifico di Mozilla: i byte 0–7 sono la firma mozLz40\0 (6D 6F 7A 4C 7A 34 30 00), i byte 8–11 rappresentano la dimensione non compressa come intero a 32 bit little-endian e il resto è un singolo blocco LZ4 grezzo, non un frame LZ4, quindi lo strumento standard da riga di comando lz4 lo rifiuta. Utilizzare uno strumento dedicato come dejsonlz4 o lz4json, oppure Python: lz4.block.decompress(data[12:], uncompressed_size=int.from_bytes(data[8:12], 'little')). In modo equivalente, lz4.block.decompress(data[8:]), perché per impostazione predefinita questa funzione legge un prefisso di dimensione di 4 byte little-endian.
Il JSON rivela windows[].tabs[], ciascuna con entries[] (la cronologia di navigazione avanti e indietro della scheda: url, title, referrer), un index che contrassegna la voce corrente e lastAccessed; _closedTabs con closedAt; _closedWindows; e formdata contenente il testo digitato nei campi delle pagine (i campi delle password non vengono salvati). I valori temporali della sessione sono espressi in millisecondi dal 1970 UTC. Il confronto tra previous.jsonlz4 e recovery.jsonlz4 mostra ciò che è cambiato tra le sessioni.
Firefox e Chromium a confronto
| File di Firefox | Equivalente in Chromium | A quali domande risponde |
|---|---|---|
places.sqlite: moz_places, moz_historyvisits | History: urls, visits | Quali pagine, quando e come sono state raggiunte |
places.sqlite: moz_annos | History: downloads, downloads_url_chains | Che cosa è stato scaricato, da dove e dove è stato salvato |
places.sqlite: moz_inputhistory | Shortcuts: omni_box_shortcuts | Che cosa ha digitato l'utente nella barra degli indirizzi |
places.sqlite: moz_bookmarks | Bookmarks (JSON) | Quali pagine sono state salvate intenzionalmente e quando |
formhistory.sqlite | Web Data: autofill | Quali valori sono stati digitati nei moduli web |
cookies.sqlite | Network\Cookies (valori cifrati) | Quali siti sono stati visitati e quali sessioni sono state mantenute |
logins.json + key4.db | Login Data + chiave di Local State | Quali credenziali sono state salvate e per quali siti |
sessionstore-backups\*.jsonlz4 | Sessions\Session_*, Tabs_* | Quali schede erano aperte e chiuse e quali dati erano presenti nei moduli delle pagine |
extensions.json | Extensions\ + Preferences | Quali componenti aggiuntivi sono stati installati |
Esempio svolto: digitazione e successivo download
Un'acquisizione di triage contiene places.sqlite e il relativo WAL. La domanda è: l'utente si è recato intenzionalmente su un sito di trasferimento file e quando è terminato un download? Eseguire:
SELECT v.id, v.from_visit, strftime('%Y-%m-%dT%H:%M:%SZ', v.visit_date/1000000, 'unixepoch') AS visit_utc, v.visit_type, p.url FROM moz_historyvisits v JOIN moz_places p ON p.id = v.place_id ORDER BY v.visit_date;
| id | from_visit | visit_utc | visit_type | url |
|---|---|---|---|---|
| 1201 | 0 | 2026-05-19T08:41:37Z | 2 | https://transfer.example.com/ |
| 1202 | 1201 | 2026-05-19T08:42:18Z | 1 | https://transfer.example.com/d/7Hq2 |
| 1203 | 1202 | 2026-05-19T08:42:25Z | 7 | https://dl.transfer.example.com/7Hq2/Q1_payroll.zip |
Il valore grezzo di visit_date della visita 1201 è 1.779.180.097.512.334. La divisione intera per 1.000.000 dà 1.779.180.097 secondi, che corrispondono a 2026-05-19T08:41:37Z; la divisione tronca gli 0,512334 s, come previsto per una risposta in secondi interi. (datetime(visit_date/1000000,'unixepoch') restituisce lo stesso istante senza T e Z.) La visita 1201 è di tipo TYPED senza referrer e moz_inputhistory contiene input = trans che punta a quella pagina, quindi l'utente ha digitato l'inizio dell'indirizzo. La visita 1202 è stata un collegamento seguito dalla visita 1201 e la visita 1203 un download avviato dalla visita 1202.
Per la voce relativa all'URL dello ZIP (la destinazione della visita 1203), downloads/metaData contiene {"state":1,"deleted":false,"endTime":1779180163904,"fileSize":48213977}. Qui endTime è in millisecondi: 1.779.180.163.904 ÷ 1.000 = 1.779.180.163,904 s, quindi il download è terminato alle 2026-05-19T08:42:43Z, 18 secondi dopo la visita di tipo 7.
Non confondere le epoche dei browser. Chromium memorizza lo stesso istante come microsecondi dal 1601: (1.779.180.097 + 11.644.473.600) × 1.000.000 + 512.334 = 13.423.653.697.512.334, e la query per Chromium deve sottrarre 11.644.473.600 dopo la divisione. Decodificando il valore di Firefox con la formula di Chromium si ottiene 1657-05-19T08:41:37Z; decodificando il valore di Chromium come PRTime si ottiene 2395-05-19T08:41:37Z. Entrambe le unità sono microsecondi, quindi soltanto l'epoca le distingue; un valore a 16 cifre nel 2026 è PRTime e un valore a 17 cifre è un valore temporale di Chromium.
Strumenti
DB Browser for SQLite esegue query come quella riportata sopra. Plaso dispone di plugin per i database SQLite di Firefox, come firefox_history e firefox_cookies; confrontare il loro output con una query manuale, perché gli schemi cambiano tra le versioni. Hindsight analizza soltanto i browser basati su Chromium e non legge i profili Firefox.
Focus sull'esame: Prepararsi a convertire i valori PRTime in formato ISO 8601 UTC, a leggere i codicivisit_type(in particolare 2 TYPED e 7 DOWNLOAD) e a recuperare il percorso di salvataggio e l'ora di completamento di un download damoz_annos, ricordando cheendTimeè in millisecondi. Potrebbe inoltre essere necessario individuare il profilo attivo tramiteprofiles.inioinstalls.ini, oppure riconoscere l'intestazionemozLz40\0e la sua lunghezza di 12 byte.
Punti chiave
- Il profilo roaming contiene i database e il profilo locale contiene la cache;
profiles.inieinstalls.iniidentificano il profilo utilizzato da ciascuna installazione. - Collegare tramite join
moz_historyvisits.place_idamoz_places.id; PRTime è espresso in microsecondi dal 1970 UTC, mentre il tempo di Chromium è espresso in microsecondi dal 1601. - Da Firefox 26, i download sono costituiti da una visita di tipo 7 più le annotazioni
downloads/destinationFileURIedownloads/metaData, conendTimein millisecondi. - La cronologia dei moduli, i valori dei cookie e i dati dei moduli della sessione sono memorizzati senza cifratura; le password salvate richiedono
key4.db. - I file di sessione sono costituiti da un'intestazione
mozLz40\0di 12 byte più un blocco LZ4 e rivelano le schede aperte, le schede chiuse e i dati digitati nei moduli. - Acquisire sempre i file
-wale-shme analizzare copie.