hankit / keepassphp
Library to parse .kdbx files in PHP
Requires
- php: >=8.4.0
- ext-hash: *
- ext-json: *
- ext-openssl: *
- ext-sodium: *
- ext-xmlreader: *
- ext-zlib: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12
README
KeePassPHP is a PHP library for reading and writing KeePass .kdbx databases.
It can:
- inspect KDBX files without decrypting them
- open KeePass 2.x databases and map them to PHP objects
- read groups, entries, passwords, custom fields, and custom icons
- write new KDBX 4.1 databases from the in-memory
Databasemodel - work with password keys, key files, and composite keys
It currently supports:
- KDBX 3.x
- KDBX 4.0 / 4.1 when the database uses:
- outer cipher
AES-256 - KDF
AES-KDF
- outer cipher
It does not currently support:
- KDBX 4 databases using
Argon2d - KDBX 4 databases using
Argon2id - KDBX 4 databases using outer
ChaCha20
Requirements
- PHP
>= 8.4 ext-hashext-jsonext-opensslext-sodiumext-xmlreaderext-zlib
Installation
composer require hankit/keepassphp
Quick Start
Open a KeePass database directly:
<?php use KeePassPHP\Database; use KeePassPHP\Keys\KeyFromPassword; use KeePassPHP\Readers\ResourceReader; $reader = ResourceReader::openFile('/path/to/database.kdbx'); if ($reader === null) { throw new RuntimeException('Unable to open database file.'); } try { $database = Database::fromKdbx( $reader, new KeyFromPassword('secret', 'SHA256'), ); } finally { $reader->close(); } echo $database->getName(); echo $database->getPassword('entry-uuid');
Use a key file:
<?php use KeePassPHP\Database; use KeePassPHP\Keys\KeyFromFile; use KeePassPHP\Readers\ResourceReader; $reader = ResourceReader::openFile('/path/to/database.kdbx'); if ($reader === null) { throw new RuntimeException('Unable to open database file.'); } $keyFileContent = file_get_contents('/path/to/database.keyx'); if ($keyFileContent === false) { throw new RuntimeException('Unable to read key file.'); } try { $database = Database::fromKdbx( $reader, new KeyFromFile($keyFileContent), ); } finally { $reader->close(); }
Use a composite key:
<?php use KeePassPHP\Database; use KeePassPHP\Keys\CompositeKey; use KeePassPHP\Keys\KeyFromFile; use KeePassPHP\Keys\KeyFromPassword; use KeePassPHP\Readers\ResourceReader; $reader = ResourceReader::openFile('/path/to/database.kdbx'); if ($reader === null) { throw new RuntimeException('Unable to open database file.'); } $keyFileContent = file_get_contents('/path/to/database.keyx'); if ($keyFileContent === false) { throw new RuntimeException('Unable to read key file.'); } $key = new CompositeKey('SHA256'); $key->addKey(new KeyFromPassword('secret', 'SHA256')); $key->addKey(new KeyFromFile($keyFileContent)); try { $database = Database::fromKdbx($reader, $key); } finally { $reader->close(); }
Inspecting A Database
KdbxInspector can read the outer header and tell you what kind of database you are dealing with before you try to decrypt it.
<?php use KeePassPHP\KdbxInspector; $metadata = KdbxInspector::inspectFile('/path/to/database.kdbx'); var_dump([ 'format' => $metadata->formatLabel, 'cipher' => $metadata->cipherName, 'kdf' => $metadata->kdfName, 'compressed' => $metadata->isCompressed, 'decryptable_here' => $metadata->isDecryptableByCurrentLibrary, ]);
For KDBX 3.x this also reports the inner random stream. For KDBX 4.x it reports the outer header information that is available without decrypting the payload.
Reading Data
The Database model gives you access to the parsed tree:
<?php foreach ($database->getGroups() as $group) { // Traverse groups and entries from here. } $password = $database->getPassword('entry-uuid'); $username = $database->getStringField('entry-uuid', 'UserName'); $customFields = $database->listCustomFields('entry-uuid');
You can also serialize the parsed database to arrays:
<?php $data = $database->toArray();
Creating Databases
New databases are created by building a Database object graph in memory and then writing it as KDBX 4.1.
At a minimum you usually:
- create a
Database - create one or more
Groupobjects - create
Entryobjects and attach them to groups - write the database with
Database::toKdbx4()
Example:
<?php use KeePassPHP\Database; use KeePassPHP\Entry; use KeePassPHP\Group; use KeePassPHP\Keys\KeyFromPassword; use KeePassPHP\Strings\UnprotectedString; $database = new Database(); $database->setName('Company Vault'); $root = new Group(); $root->uuid = base64_encode(random_bytes(16)); $root->name = 'Root'; $servers = new Group(); $servers->uuid = base64_encode(random_bytes(16)); $servers->name = 'Servers'; $entry = new Entry(); $entry->uuid = base64_encode(random_bytes(16)); $entry->tags = 'production;linux'; $entry->setStringField(Database::KEY_TITLE, new UnprotectedString('Web 01')); $entry->setStringField(Database::KEY_USERNAME, new UnprotectedString('deploy')); $entry->setStringField(Database::KEY_URL, new UnprotectedString('ssh://web-01.internal')); $entry->setStringField('Environment', new UnprotectedString('production')); $entry->setPassword(new UnprotectedString('secret-password')); $servers->addEntry($entry); $root->addGroup($servers); $database->addGroup($root); $payload = $database->toKdbx4( new KeyFromPassword('master-password', 'SHA256'), );
Useful model methods:
Database::setName()sets the database name shown in KeePassDatabase::addGroup()adds a top-level groupDatabase::setCustomIcon()registers a custom icon by UUIDGroup::addGroup()adds a child groupGroup::addEntry()adds an entryEntry::setPassword()sets the password fieldEntry::setStringField()sets standard or custom string fieldsEntry::addHistoryEntry()adds a history item
Notes:
uuid,customIcon, and related UUID fields are expected to be base64-encoded 16-byte values, matching the KeePass XML format- if you do not set a UUID on a group or entry, the writer will generate a deterministic UUID from the current value or a random UUID if the field is empty
- password values are written as protected values in KDBX 4 output
- non-password string fields are written as plain string values unless you pass a protected boxed string yourself
You can control KDBX 4 writing with Kdbx4WriteOptions:
<?php use KeePassPHP\Kdbx4WriteOptions; $payload = $database->toKdbx4( new KeyFromPassword('master-password', 'SHA256'), new Kdbx4WriteOptions( rounds: 10000, compress: true, ), );
Encryption Support
The low-level KdbxFile entrypoint can decrypt both supported KDBX 3 and KDBX 4 files and dispatches to the correct implementation automatically.
Creating encrypted files in the old KDBX 3 format is available through KdbxFile::forEncryption():
<?php use KeePassPHP\KdbxFile; use KeePassPHP\Keys\KeyFromPassword; $file = KdbxFile::forEncryption(6000); $payload = $file->encrypt('<KeePassFile />', new KeyFromPassword('secret', 'SHA256'));
Creating KDBX 4.1 databases is available through the Database model:
<?php use KeePassPHP\Database; use KeePassPHP\Entry; use KeePassPHP\Group; use KeePassPHP\Kdbx4WriteOptions; use KeePassPHP\Keys\KeyFromPassword; use KeePassPHP\Strings\UnprotectedString; $database = new Database(); $database->setName('Generated Database'); $root = new Group(); $root->name = 'Root'; $entry = new Entry(); $entry->setStringField(Database::KEY_TITLE, new UnprotectedString('Example')); $entry->setStringField(Database::KEY_USERNAME, new UnprotectedString('alice')); $entry->setPassword(new UnprotectedString('secret-password')); $root->addEntry($entry); $database->addGroup($root); $payload = $database->toKdbx4( new KeyFromPassword('master-password', 'SHA256'), new Kdbx4WriteOptions(), );
Supported Formats
KDBX 3.x
Supported for reading and decryption.
Supported for low-level encryption through KdbxFile::forEncryption().
KDBX 4.0 / 4.1
Supported for reading and decryption when the outer header uses:
- cipher
AES-256 - KDF
AES-KDF
Inner protected values are supported for the current KDBX 4 read path, including KeePass files that use ChaCha20 as the inner random stream.
Supported for writing in KDBX 4.1 with:
- outer cipher
AES-256 - KDF
AES-KDF - inner random stream
Salsa20by default
Limitations
- KDBX 4 with
Argon2dthrows an exception during decryption - KDBX 4 with
Argon2idthrows an exception during decryption - KDBX 4 with outer
ChaCha20throws an exception during decryption - KDBX 4 writing currently targets
4.1withAES-KDFand outerAES-256only - database name and other XML metadata are only available after successful decryption
Error Handling
The modernized API throws exceptions for invalid input, unsupported formats, and decryption failures.
The main exception type is:
KeePassPHP\Exceptions\KeePassPHPException
Development
composer test
composer stan
composer cs:check
composer cs:fix
composer check
There is also an opt-in local playground for manually testing real databases:
cp playground/keepass-databases.php.dist playground/keepass-databases.php
# edit playground/keepass-databases.php
composer test:playground
Breaking Changes
Recent API and behavior changes are documented in BREAKING_CHANGES.md.
License
MIT