Encrypted SQLite for PHP, done properly
From a sed-and-compile script to a hardened Docker-and-Qubes packaging pipeline tracking SQLCipher across seven PHP versions and five Linux distributions.
The beginning
This project began in early 2013, originally for
Mydex Data Services CIC
. The goal was simple: build PHP’s pdo_sqlite
(and later sqlite3) extensions linked against
SQLCipher
instead of stock SQLite, so PHP applications
could use encrypted databases without changing their database driver code.
A key design decision that suits my needs but may not for you: the packages are drop-in
replacements. The driver name doesn’t change - it’s still sqlite3 and pdo_sqlite.
The benefit is that applications that open an SQLite PDO connection don’t need modification;
they just need to send PRAGMA key to enable SQLCipher encryption. Regular unencrypted SQLite
databases still work.
If instead you want it to install a sqlcipher driver name, you may have to make some tweaks.
Managing the compilation steps (and the building of the deb packages) used to be so painful, I actively avoided it. But eventually I pulled my finger out, and now it is quite easy!
The build pipeline
Because this is a security-focused package, the build process itself needed a security model. The way I do it:
- Docker builds using the gvisor/runsc hardened container runtime.
- The actual compile and deb build steps run as an unprivileged user inside the container,
with no network access (
--network=none). Network is only enabled to install build dependencies. - The Docker daemon runs inside an ephemeral, disposable QubesOS VM with only outbound port 80/443 access.
- The GPG signing key lives on a Yubikey, accessed via Qubes split GPG - the build VM has no filesystem access to the private key.
- Signing and APT repo preparation happen in a separate Qubes VM with no network access at all.
The Dockerfile uses a multi-stage build. The build stage installs dependencies, clones
SQLCipher at the target version tag, fetches the PHP source via apt-get source, then switches
to an unprivileged builder user to run dpkg-buildpackage and lintian with
--network=none. An autopkgtest run validates the package can actually open an encrypted
database. The final stage is FROM scratch, containing only the built .deb, .buildinfo, and
.changes files.
scripts/package.sh orchestrates the matrix: at time of writing, it iterates over five base
images (Debian 13 trixie, Debian 12 bookworm, Ubuntu 26.04 resolute, Ubuntu 24.04 noble, Ubuntu
22.04 jammy) and seven PHP versions (7.4 through 8.5), building a tagged image for each combination
and exporting the artifacts to the host.
Why it matters
SQLCipher gives you transparent, AES-256 encryption at the file level - the database is encrypted on disk, and the key never touches storage. For PHP applications that already use SQLite (and there are many), this is the cheapest path to encryption-at-rest without changing the database driver or migrating to a server-based database.
The packages are published at apt.mig5.net, but the entire build process is in the open at
git.mig5.net/mig5/php-sqlcipher
. If you don’t trust my
repository, you can build them yourself - that’s the point.
Have fun adding encryption to your SQLite databases!
- Started: early 2013
- PHP: 7.4, 8.0–8.5
- Distros: Debian 12/13, Ubuntu 22.04/24.04/26.04
- Build: Docker + gvisor/runsc in QubesOS