Navegadores, correo y sincronización en la nube · Lección 2 de 6
Firefox: places.sqlite y datos de sesión
11 min de lectura
Firefox conserva su historial en su propio esquema SQLite, sus propios archivos JSON y un formato de sesión comprimido, con marcas de tiempo contadas desde una época distinta de la de Chromium. Esta lección muestra dónde se encuentra un perfil de Firefox, qué tablas responden a «qué se visitó, escribió y descargó, y cuándo», y cómo abrir los archivos de sesión que registran las pestañas abiertas y cerradas recientemente. La lección 4.3 aborda la caché y la navegación privada.
Localización del perfil
En Windows, cada perfil tiene dos carpetas con el mismo nombre: %APPDATA%\Mozilla\Firefox\Profiles\<random>.<name> (itinerante: las bases de datos y los archivos JSON de esta lección) y %LOCALAPPDATA%\Mozilla\Firefox\Profiles\<random>.<name> (local: la caché). El prefijo consta de ocho caracteres aleatorios; las versiones actuales denominan default-release al perfil creado para cada instalación, por ejemplo, k3x9p2qa.default-release.
Dos archivos de %APPDATA%\Mozilla\Firefox\ indican qué perfil se utilizaba. profiles.ini enumera todos los perfiles en secciones [ProfileN] (Name, IsRelative, Path) y, desde Firefox 67, incluye una sección [Install<hash>] cuya línea Default= indica el perfil utilizado por una instalación. installs.ini repite esa correspondencia, con una sección por cada hash de instalación. Un perfil llamado default con Default=1 suele ser un perfil heredado casi vacío, por lo que debe seguir la entrada [Install…] y, aun así, examinar todas las carpetas de perfiles presentes.
En Linux, los perfiles se encuentran en ~/.mozilla/firefox/ y la caché en ~/.cache/mozilla/firefox/. El paquete Snap utiliza ~/snap/firefox/common/.mozilla/firefox/ y el paquete Flatpak, ~/.var/app/org.mozilla.firefox/.mozilla/firefox/. A partir de Firefox 147, las instalaciones nuevas siguen la estructura XDG en ~/.config/mozilla/firefox/. Busque todos los archivos profiles.ini en lugar de confiar en una sola ruta.
La mayoría de las bases de datos de Firefox funcionan en modo de registro de escritura anticipada. Recopile places.sqlite junto con places.sqlite-wal y places.sqlite-shm (haga lo mismo con las cookies, el historial de formularios y los favicons): las visitas recientes pueden existir únicamente en el WAL. Calcule los hashes del conjunto y trabaje después sobre copias, porque abrir una base de datos con SQLite puede consolidar el WAL en el archivo principal y modificar ambos.
places.sqlite
moz_places y moz_historyvisits
moz_places contiene una fila por URL: url, title, rev_host (el nombre del host invertido carácter por carácter más un punto final, de modo que transfer.example.com se convierte en moc.elpmaxe.refsnart.), visit_count, typed (1 si la URL se escribió alguna vez), frecency (una puntuación de clasificación para las sugerencias de la barra de direcciones, no una hora), last_visit_date y guid. moz_historyvisits contiene una fila por visita: place_id (se une con moz_places.id), visit_date, visit_type y from_visit (el id de la visita que condujo hasta aquí; 0 si no hubo ninguna). Ambas fechas son PRTime: microsegundos desde 1970-01-01 UTC (lección 3.1).
| visit_type | Nombre | Significado |
|---|---|---|
| 1 | LINK | Se siguió un enlace |
| 2 | TYPED | Se escribió o seleccionó en la barra de direcciones |
| 3 | BOOKMARK | Se abrió desde un marcador |
| 4 | EMBED | Contenido incrustado; las versiones actuales lo mantienen en memoria, por lo que rara vez aparece en disco |
| 5 | REDIRECT_PERMANENT | Se accedió mediante una redirección permanente, como HTTP 301 |
| 6 | REDIRECT_TEMPORARY | Se accedió mediante una redirección temporal, como HTTP 302 |
| 7 | DOWNLOAD | URL solicitada como descarga |
| 8 | FRAMED_LINK | Se siguió un enlace dentro de un marco |
| 9 | RELOAD | Se recargó la página |
Seguir from_visit hacia atrás permite reconstruir una cadena de navegación. Firefox elimina automáticamente el historial antiguo, por lo que la ausencia de una visita no demuestra que no se haya producido.
Marcadores, texto escrito y orígenes
moz_bookmarks almacena marcadores (type 1), carpetas (2) y separadores (3), con fk que apunta a moz_places.id, parent, title, y dateAdded y lastModified en PRTime. moz_inputhistory registra lo que el usuario escribió en la barra de direcciones y qué resultado eligió después: input (el texto escrito), place_id (la página seleccionada) y use_count, una puntuación que disminuye con el tiempo. No tiene marca de tiempo, por lo que debe fecharlo mediante las visitas correspondientes. moz_origins conserva una fila por combinación de esquema y host (prefix, host, frecency), a la que hace referencia moz_places.origin_id.
Descargas
Desde Firefox 26, las descargas se registran en places.sqlite; los perfiles antiguos utilizaban un archivo downloads.sqlite independiente. Una descarga añade una visita de tipo 7 para la URL de origen y dos anotaciones de página en moz_annos, vinculadas al place_id de esa URL y denominadas mediante moz_anno_attributes.name:
downloads/destinationFileURI:contentcontiene la ubicación de guardado como URI, por ejemplo,file:///C:/Users/jsilva/Downloads/Q1_payroll.zip.downloads/metaData:contentcontiene JSON constate(1 finalizada, 2 fallida, 3 cancelada; otros valores indican descargas en pausa o bloqueadas),endTimeen milisegundos desde 1970 UTC,fileSize(presente cuando la descarga se completó correctamente) ydeleted.
Las filas de moz_annos también contienen dateAdded y lastModified en PRTime. Corrobore los datos con el sistema de archivos: la entrada del archivo en $MFT y su flujo Zone.Identifier (lección 1.4).
Otros archivos del perfil
favicons.sqlite: iconos (moz_icons) vinculados a URL de páginas (moz_pages_w_icons), un rastro que corrobora que una página se cargó o se añadió a marcadores.formhistory.sqlite:moz_formhistoryalmacena valores escritos en campos de formularios web:fieldname,value,timesUsed,firstUsedylastUsed(PRTime). Los cuadros de búsqueda también aparecen aquí, a menudo con el valorsearchbar-historyenfieldname.cookies.sqlite:moz_cookiesconhost,name,value,creationTimeylastAccessed(PRTime), yexpiry. Los valores no están cifrados. Las versiones antiguas almacenanexpiryen segundos Unix y las actuales en milisegundos, por lo que debe comprobar la cantidad de dígitos.permissions.sqlite:moz_permsregistra los permisos de los sitios (origin,type, comodesktop-notificationocamera,permission,modificationTime).logins.json: credenciales guardadas.hostname,formSubmitURLy las horas son legibles;encryptedUsernameyencryptedPasswordestán cifrados con una clave almacenada enkey4.db(base de datos de claves NSS, en SQLite desde Firefox 58), protegida por la contraseña principal si se ha establecido una.extensions.json: complementos instalados conid,version,active,installDateyupdateDate(milisegundos), ypath; los paquetes se encuentran enextensions\como archivos.xpi.addons.jsonalmacena en caché metadatos de la tienda de complementos. Busque complementos que el usuario no habría instalado de forma consciente.
Datos de sesión
La carpeta sessionstore-backups contiene el estado de la sesión:
recovery.jsonlz4: la sesión activa, que se reescribe mientras Firefox se ejecuta (cada 15 segundos de forma predeterminada).recovery.baklz4: la copia anterior del archivo de recuperación.previous.jsonlz4: la sesión anterior al último reinicio.upgrade.jsonlz4-<buildID>: una copia creada cuando se actualizó Firefox, cuyo nombre incluye el identificador de compilación de 14 dígitos.- Al cerrarse correctamente, Firefox también escribe
sessionstore.jsonlz4en la raíz del perfil.
Los archivos utilizan el contenedor LZ4 propio de Mozilla: los bytes 0–7 son la firma mágica mozLz40\0 (6D 6F 7A 4C 7A 34 30 00), los bytes 8–11 son el tamaño sin comprimir como un entero de 32 bits en little-endian, y el resto es un único bloque LZ4 sin encapsular, no una trama LZ4, por lo que la herramienta estándar de línea de comandos lz4 lo rechaza. Utilice una herramienta específica, como dejsonlz4 o lz4json, o Python: lz4.block.decompress(data[12:], uncompressed_size=int.from_bytes(data[8:12], 'little')). De forma equivalente, lz4.block.decompress(data[8:]), porque esa función lee de forma predeterminada un prefijo de tamaño de 4 bytes en little-endian.
El JSON revela windows[].tabs[], cada una con entries[] (el historial de retroceso y avance de la pestaña: url, title, referente), un index que señala la entrada actual y lastAccessed; _closedTabs con closedAt; _closedWindows; y formdata, que contiene el texto escrito en los campos de la página (los campos de contraseña no se guardan). Las horas de sesión se expresan en milisegundos desde 1970 UTC. Comparar previous.jsonlz4 con recovery.jsonlz4 muestra qué cambió entre sesiones.
Comparación entre Firefox y Chromium
| Archivo de Firefox | Equivalente en Chromium | Qué permite responder |
|---|---|---|
places.sqlite: moz_places, moz_historyvisits | History: urls, visits | Qué páginas se visitaron, cuándo y cómo se llegó a ellas |
places.sqlite: moz_annos | History: downloads, downloads_url_chains | Qué se descargó, desde dónde y dónde se guardó |
places.sqlite: moz_inputhistory | Shortcuts: omni_box_shortcuts | Qué escribió el usuario en la barra de direcciones |
places.sqlite: moz_bookmarks | Bookmarks (JSON) | Qué páginas se guardaron deliberadamente y cuándo |
formhistory.sqlite | Web Data: autofill | Qué valores se escribieron en formularios web |
cookies.sqlite | Network\Cookies (valores cifrados) | Qué sitios se visitaron y qué sesiones se mantuvieron |
logins.json + key4.db | Login Data + clave de Local State | Qué credenciales se guardaron y a qué sitios pertenecen |
sessionstore-backups\*.jsonlz4 | Sessions\Session_*, Tabs_* | Qué pestañas estaban abiertas y cerradas, y los datos de formularios de las páginas |
extensions.json | Extensions\ + Preferences | Qué complementos estaban instalados |
Ejemplo resuelto: escritura seguida de descarga
Una recopilación de triaje contiene places.sqlite y su WAL. La pregunta es: ¿accedió el usuario deliberadamente a un sitio de transferencia de archivos y cuándo terminó una descarga? Ejecute:
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 |
El valor sin procesar de visit_date de la visita 1201 es 1.779.180.097.512.334. La división entera entre 1.000.000 da 1.779.180.097 segundos, que corresponden a 2026-05-19T08:41:37Z; la división trunca los 0,512334 s, como corresponde a una respuesta en segundos enteros. (datetime(visit_date/1000000,'unixepoch') da el mismo instante sin la T ni la Z.) La visita 1201 es TYPED sin referente, y moz_inputhistory tiene input = trans apuntando a esa página, por lo que el usuario escribió el comienzo de la dirección. La visita 1202 fue un enlace seguido desde 1201, y la visita 1203, una descarga iniciada desde 1202.
Para el registro de la URL del ZIP (el destino de la visita 1203), downloads/metaData contiene {"state":1,"deleted":false,"endTime":1779180163904,"fileSize":48213977}. Aquí endTime está en milisegundos: 1.779.180.163.904 ÷ 1.000 = 1.779.180.163,904 s, por lo que la descarga terminó a las 2026-05-19T08:42:43Z, 18 segundos después de la visita de tipo 7.
No mezcle las épocas de los navegadores. Chromium almacena el mismo instante como microsegundos desde 1601: (1.779.180.097 + 11.644.473.600) × 1.000.000 + 512.334 = 13.423.653.697.512.334, y la consulta de Chromium debe restar 11.644.473.600 después de dividir. Si decodifica el valor de Firefox con la fórmula de Chromium, obtiene 1657-05-19T08:41:37Z; si decodifica el valor de Chromium como PRTime, obtiene 2395-05-19T08:41:37Z. Ambas unidades son microsegundos, por lo que solo la época las distingue; un valor de 16 dígitos en 2026 es PRTime y uno de 17 dígitos es tiempo de Chromium.
Herramientas
DB Browser for SQLite ejecuta consultas como la anterior. Plaso tiene complementos para SQLite de Firefox, como firefox_history y firefox_cookies; compare sus resultados con una consulta manual, porque los esquemas cambian entre versiones. Hindsight analiza únicamente navegadores basados en Chromium y no lee perfiles de Firefox.
Enfoque del examen: Prepárese para convertir valores PRTime a ISO 8601 UTC, interpretar códigosvisit_type(especialmente 2 TYPED y 7 DOWNLOAD) y recuperar la ruta de guardado y la hora de finalización de una descarga a partir demoz_annos, recordando queendTimeestá en milisegundos. También puede tener que localizar el perfil activo a partir deprofiles.inioinstalls.ini, o reconocer la cabeceramozLz40\0y su longitud de 12 bytes.
Puntos clave
- El perfil itinerante contiene las bases de datos y el perfil local contiene la caché;
profiles.inieinstalls.iniidentifican el perfil que utilizó cada instalación. - Una
moz_historyvisits.place_idconmoz_places.id; PRTime se expresa en microsegundos desde 1970 UTC y el tiempo de Chromium, en microsegundos desde 1601. - Desde Firefox 26, las descargas consisten en una visita de tipo 7 más las anotaciones
downloads/destinationFileURIydownloads/metaData, conendTimeen milisegundos. - El historial de formularios, los valores de cookies y los datos de formularios de la sesión se almacenan sin cifrar; las contraseñas guardadas requieren
key4.db. - Los archivos de sesión constan de una cabecera
mozLz40\0de 12 bytes más un bloque LZ4, y revelan pestañas abiertas, pestañas cerradas y datos escritos en formularios. - Recopile siempre los archivos
-waly-shmy analice copias.