Skip to main content

firefox-logins

Read and write Firefox saved passwords, by driving the browser's own NSS Secret Decoder Ring through ctypes.

Plenty of tools decrypt logins.json. This one also seals new credentials into it, using the same code path Firefox itself uses — so entries it writes are indistinguishable from ones you saved by hand. That makes browser-to-browser password migration possible without a plaintext CSV round trip.

No runtime dependencies: the crypto comes from the Firefox install already on the machine.

Install

pip install firefox-logins

Requires Python 3.11+ and a Firefox installation. Tested on macOS; Linux paths are supported but less exercised.

Usage

Read

from firefox_logins import LoginStore

with LoginStore.open() as store:
    for login in store:
        print(login.hostname, login.username, login.password)

LoginStore.open() uses the profile Firefox would launch. Pick another with find_profiles(), and pass primary_password= if the profile has one.

Write

from firefox_logins import Login, LoginStore

with LoginStore.open() as store:
    store.add(Login(
        hostname="https://example.com",
        username="ada",
        password="hunter2",
        form_submit_url="https://example.com",
    ))
    written = store.save()   # Firefox must be quit

save() decrypts every entry — existing and new — before replacing the file, and abandons the write if any fails. It backs up to logins.json.bak, writes to a staging file, then renames, so an interrupted run cannot leave a partial file where your credentials were.

Set exactly one of form_submit_url (form logins) or http_realm (HTTP auth); Firefox rejects entries with both or neither, so the constructor does too.

Profiles

from firefox_logins import find_profiles

for profile in find_profiles():
    print(profile.name, profile.path, profile.is_default)

Low-level

from firefox_logins import SecretDecoderRing, keyid_from_blob

with SecretDecoderRing("/path/to/profile") as sdr:
    sdr.selftest()
    blob = sdr.encrypt("secret")
    assert sdr.decrypt(blob) == "secret"

Notes on the write path

Four things are needed to seal a value the way Firefox does, none of them covered by the read-only references this was built against. They are recorded here because each one fails in a way that is easy to misdiagnose:

  1. libnss3 declares @rpath/libmozglue, which the loader cannot resolve from a host process. The dependency is loaded by absolute path first, and the working directory stays inside the Firefox bundle through NSS_Init, because NSS dlopens softokn and freebl during initialisation.
  2. Firefox does not export PK11SDR_Encrypt — only PK11SDR_EncryptWithMechanism.
  3. The mechanism must be CKM_AES_CBC. CKM_AES_CBC_PAD and both DES3 forms return SEC_ERROR_INVALID_ARGS, because the Secret Decoder Ring applies PKCS#7 padding itself rather than delegating it to PKCS#11.
  4. The key id must be non-empty. NSS's default is f8000000000000000000000000000001; keyid_from_blob() recovers a profile's own from any entry it already holds, since the stored value is DER SEQUENCE { OCTET STRING keyid, AlgorithmIdentifier, OCTET STRING ciphertext }.

selftest() runs a seal/unseal round trip and is called automatically when a LoginStore is opened, so a wrong mechanism or key id fails immediately rather than producing a file Firefox silently cannot read.

Scope and safety

This reads and writes credentials belonging to the user running it, using keys already present in that user's own profile. It grants no access that running as that user does not already imply, and it cannot bypass a Primary Password — without the correct one, NSS refuses to unlock the key slot.

If a profile has a Primary Password, pass it:

LoginStore.open(primary_password="…")

Quit Firefox before writing. Firefox rewrites logins.json on exit, so changes made while it runs are discarded; save() refuses by default and explains why.

Companion

chromium-reader does the equivalent job for Chromium's on-disk storage formats.

License

MIT

Metadata

Release files for firefox-logins 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for firefox-logins 0.1.0
File Size Uploaded
firefox_logins-0.1.0.tar.gz 17.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for firefox-logins 0.1.0
File Interpreter ABI Platform
firefox_logins-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.0 kB

Release files / firefox_logins-0.1.0.tar.gz

Download URL firefox_logins-0.1.0.tar.gz
Size 17.5 kB
Tags Source
SHA-256 checksum
How to use checksums
8230db7ace1ad0f465b5a9a1a4ce26c8518b2d8f19563cba21ee1d67fe7fdd3e
BLAKE2b-256 checksum
How to use checksums
35cf7171cb2653b62336d73f60a7c8c50682012c6eb5a7f1fd7f35c9482cdcb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release files / firefox_logins-0.1.0-py3-none-any.whl

Download URL firefox_logins-0.1.0-py3-none-any.whl
Size 14.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df167f00a1784194764d3d537114b658c497c8e8f71823d0f440f4cdd585718b
BLAKE2b-256 checksum
How to use checksums
5b6b6aa0bc13da0ecce050c52eb10faede93614d1170a4a7cb8517306befa44b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page