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:
libnss3declares@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 throughNSS_Init, because NSSdlopens softokn and freebl during initialisation.- Firefox does not export
PK11SDR_Encrypt— onlyPK11SDR_EncryptWithMechanism. - The mechanism must be
CKM_AES_CBC.CKM_AES_CBC_PADand both DES3 forms returnSEC_ERROR_INVALID_ARGS, because the Secret Decoder Ring applies PKCS#7 padding itself rather than delegating it to PKCS#11. - 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 DERSEQUENCE { 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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file firefox_logins-0.1.0.tar.gz.
File metadata
- Download URL: firefox_logins-0.1.0.tar.gz
- Upload date:
- Size: 17.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8230db7ace1ad0f465b5a9a1a4ce26c8518b2d8f19563cba21ee1d67fe7fdd3e
|
|
| MD5 |
32ee760e122a5d0c9ab8c869ab1b6cdc
|
|
| BLAKE2b-256 |
35cf7171cb2653b62336d73f60a7c8c50682012c6eb5a7f1fd7f35c9482cdcb3
|
Provenance
The following attestation bundles were made for firefox_logins-0.1.0.tar.gz:
Publisher:
publish.yml on DJRHails/firefox-logins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
firefox_logins-0.1.0.tar.gz -
Subject digest:
8230db7ace1ad0f465b5a9a1a4ce26c8518b2d8f19563cba21ee1d67fe7fdd3e - Sigstore transparency entry: 2583226220
- Sigstore integration time:
-
Permalink:
DJRHails/firefox-logins@0ba095d1fed2ed76020de605e5258cf19a213ebd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/DJRHails
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0ba095d1fed2ed76020de605e5258cf19a213ebd -
Trigger Event:
push
-
Statement type:
File details
Details for the file firefox_logins-0.1.0-py3-none-any.whl.
File metadata
- Download URL: firefox_logins-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df167f00a1784194764d3d537114b658c497c8e8f71823d0f440f4cdd585718b
|
|
| MD5 |
a5b8b442f602938e462165a59642bcc5
|
|
| BLAKE2b-256 |
5b6b6aa0bc13da0ecce050c52eb10faede93614d1170a4a7cb8517306befa44b
|
Provenance
The following attestation bundles were made for firefox_logins-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on DJRHails/firefox-logins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
firefox_logins-0.1.0-py3-none-any.whl -
Subject digest:
df167f00a1784194764d3d537114b658c497c8e8f71823d0f440f4cdd585718b - Sigstore transparency entry: 2583226260
- Sigstore integration time:
-
Permalink:
DJRHails/firefox-logins@0ba095d1fed2ed76020de605e5258cf19a213ebd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/DJRHails
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0ba095d1fed2ed76020de605e5258cf19a213ebd -
Trigger Event:
push
-
Statement type: