More and more frequently, when I ask friends and family (people with a mainly non-computing background) how they manage their passwords their eyes cloud over, and I then feel the need to tell them that they ought to apply good password hygiene. (I tend to mensplain a bit.) As such I’ve been looking much more deeply into KeePassXC as a multi-platform, Open Source, and very decent password manager.

I ran away from 1Password many years ago when, IIRC, forced cloud upon their users and also converted to a subscription model and settled for EnPass at the time. Aside from a number of UI quirks in EnPass I’ve been happy enough with it, and I got it at the time when they had a purchase model; I believe that has meanwhile also changed to a subscription model. I want to be able to recommend a program which has a fixed price (Open Source is fine) and a UI which will hopefully remain somewhat consistent. I think KeePassXC matches the requirement.

These notes are intended as a reminder to myself of the features and possibilities I discovered in KeePassXC. (Start with some screenshots if you like.)

the database

KeePassXC databases (*.kdbx; file format explained) are protected with either a password or a key file or both. The desktop app and the CLI program can optionally create these key files, and they contain 128 byte of random data which is used to augment the password. These key files can also be an image, a love letter — any file which doesn’t change. Think of it as a really complicated password that is read from a file, so you don’t have to remember or type it into your master password field.

I would likely suggest a key file created with random data and have a backup of the key file printed on paper (using a font with which I can easily differentiate zero and oh and one and ell …):

$ keyfile=kp.key
$ dd if=/dev/urandom bs=128 count=1 status=none of=$keyfile

$ openssl dgst -sha1 $keyfile
SHA1(kp.key)= f4e8b1dca0f2833d0596ba60664999fc0ca99a09

$ openssl enc -base64 -in $keyfile

$ openssl enc -A -base64 -in $keyfile |
       qrencode -l Q -o $keyfile.png

QR-code of base64 of the key file

In order to recover the binary key file I could scan the QR code, copy the resulting text (or even enter it manually from the base64 representation if necessary), and decode the base64 back into the key file’s data with

$ openssl enc -d -A -base64 -in /tmp/paper -out kp-new.key

$ openssl dgst -sha1 kp-new.key
SHA1(kp-new.key)= f4e8b1dca0f2833d0596ba60664999fc0ca99a09

KeePassXC databases can be synchronized via, say, syncthing, Dropbox, or a file share, etc., but the key files ought to be kept separately. Key files are also supported by KeePassium on iOS and possibly also by other apps such KeePassDX and KeePass2Android on Android, and Strongbox on iOS.

Hardware key chooser when opening database

In addition to password and/or key file, the desktop apps can use a supported Yubikey (I chose a 5c nano) with HMAC-SHA1 to add additional entropy to the chosen password. While this works very well it has the disadvantage of not being supported by the mobile apps I looked at, meaning it would be a desktop-only feature. Also, it’s important to have a backup Yubikey (5c NFC here) for the feature; I wouldn’t want a lost/broken Yubikey to lock me out of the database!

Sadly, KeePassXC relies on external file synchronization, which might not be trivial to set up. As Alexander notes, the original Keepass2 (and Keepass2Android) are able to open database files directly from a WebDAV URL, and merge changes that have been made from a different device, but KeePassXC is likely the more modern choice with more features.

Python module

The Python pykeepass module interacts with KeePass databases (supports KDBX3 and KDBX4), and as such also works with KeePassXC. I can create a database (this is how I created the jane.kdbx database for the examples on this page), create and find entries, add entries, change / add passwords and entry details, etc.

#!/usr/bin/env python3

from pykeepass import PyKeePass, create_database
import secrets
from xkcdpass import xkcd_password as xp

kp = create_database("jane.kdbx", password="tt", keyfile="kp.key", transformed_key=None)

g_work = kp.add_group(kp.root_group, "Work")
g_play = kp.add_group(kp.root_group, "Play")
g_social = kp.add_group(g_play, "socialmedia")

wordfile = xp.locate_wordfile()
mywords = xp.generate_wordlist(wordfile=wordfile, min_length=5, max_length=8)

password = xp.generate_xkcdpassword(mywords, acrostic="tonic", delimiter="-")

entry = kp.add_entry(g_work, "gmail", "myusername", password)
print(entry)  # Entry: "email/gmail (myusername)"

e = kp.add_entry(g_social, "Mastodon", "janej", secrets.token_urlsafe(32))

e.url = ""
e.tags = [ "fediverse", "mastodon" ]
e.notes = "account created in 2018 with 2FA"

emails = [ "", "" ]
e.set_custom_property("mail", "\n".join(emails))    # custom property expects newline-separated
e.set_custom_property("uid", "12345678")            # pykeepass > 4.0.3 will have: protect=True

# there doesn't appear to be a way of exiting cleanly without the .save()


keepassxc-cli is a command-line tool for KeePassXC from which I can manipulate its databases.

$ kpc open -k tt.key jane.kdbx
Enter password to unlock jane.kdbx:
Passwords> help

Available commands:
add                 Add a new entry to a database.
analyze             Analyze passwords for weaknesses and problems.
attachment-export   Export an attachment of an entry.
attachment-import   Imports an attachment to an entry.
attachment-rm       Remove an attachment of an entry.
clip                Copy an entry's attribute to the clipboard.
close               Close the currently opened database.
db-create           Create a new database.
db-edit             Edit a database.
db-info             Show a database's information.
diceware            Generate a new random diceware passphrase.
edit                Edit an entry.
estimate            Estimate the entropy of a password.
exit                Exit interactive mode.
generate            Generate a new random password.
help                Display command help.
ls                  List database entries.
merge               Merge two databases.
mkdir               Adds a new group to a database.
mv                  Moves an entry to a new group.
open                Open a database.
quit                Exit interactive mode.
rm                  Remove an entry from the database.
rmdir               Removes a group from a database.
search              Find entries quickly.
show                Show an entry's information.
Passwords> generate

Using the --yubikey option, I can also unlock a Yubikey-protected database from the command-line:

$ kpc ls other.kdbx -y 2
Enter password to unlock other.kdbx:
Please present or touch your YubiKey to continue.

In the example which follows, I attach an image to the database and then display all details (also the protected fields) of an entry.

$ alias kpc=/Applications/
$ kpc attachment-import -k kp.key jane.kdbx Mastodon mascot mastodon-mascot.jpg
Enter password to unlock jane.kdbx:
Successfully imported attachment mastodon-mascot.jpg as mascot to entry Mastodon.

$ kpc show jane.kdbx -k kp.key --show-protected --show-attachments Mastodon
Enter password to unlock jane.kdbx:
Title: Mastodon
UserName: janej
Password: REH1I0xz_iEM2VYvhiwfah5Rt1RROxqErmejlaoKY6A
Notes: account created in 2018 with 2FA
Uuid: {95275776-9a50-11ed-add7-f01898ef9fe7}
Tags: fediverse,mastodon

  mascot (3.0 KiB)

It’s not actually documented anywhere that I could find, but keepassxc-cli actually reads passwords from stdin. (tt is the database password and the dice ware subcommand creates four words as in ”subpar amusement crayfish footrest”.)

$ (echo tt; kpc diceware -W 4) | kpc edit -k tt.key jane.kdbx -p gmail
Enter password to unlock jane.kdbx:
Enter new password for entry:
Successfully edited entry gmail.

I could add -q to the command to completely silence prompts for database and entry’s new password.

the UI

screenshot of KeePassXC with the programmatically-created entry shown

  1. favicon downloaded from within the entry (add URL, hit download), but there is a menu for downloading URLs automatically, not possible here b/c it’s a fake address
  2. I didn’t understand the color square, but it’s a password-quality indicator
  3. Additional attributes. In future the Python module will be able to add protection (such as shown in the comment for 4.)
  4. Attachment names
  5. Group folders as created within Python

SSH agent

KeePassXC implements support for an SSH-agent, and I find it works very well. What I particularly appreciate is the possibility to override the agent socket path as I have a bit of a convoluted setup here which sets a specific path on login.

KeePassXC’s implementation can add SSH keys when unlocking a database, it can automatically remove keys from the agent after a selectable time, and it can optionally remove all keys it’s added when the database is locked (i.e. closed). Note there’s a setting with KeePassXC which can optionally ask for confirmation before using a key, but that requires the separate SSH ask-pass utility — this is not something that KeePassXC can implement as there’s no feedback from the agent itself.

There’s a very good writeup of how to manage SSH keys with KeePassXC.


KeePassXC has built-in support for Time-based One-Time Passwords (TOTP). These are passwords which use the current time as a source of uniqueness. I prefer to use an app which does TOTP than to use SMS for two-factor authentication (2FA).

I have some doubts about the security of having TOTP within the password manager (I use Authy and not the support built-in to EnPass), so I asked on Mastodon:

Is there a consensus on whether it is better/safer to have TOTP generation done within the password safe (e.g. KeePassXC) or rather externally using a separate program (e.g. Authy)?

I think I’m summarizing correctly when I quote Thomas, who responded:

It’s better to have it on a separate device. But it’s also better to have it in any place than to not have it at all.

That’s probably very good advice.

Further reading

passwords :: 22 Jan 2023 :: e-mail