fix: create the runtime directories without world access - #902
Conversation
The entrypoint creates four runtime directories, chowns them to www-data, and chmods exactly one. Measured on the running container: config is 0750, var/backup, var/cache and var/temp are 0755. var/backup holds the backup archives — the database dump and config.xml — and the XML export, which carries every account's encrypted secret and key, and with no export password the name, login, URL and notes of every account in the clear. Those files are restricted to their owner, but only once fully written: the archive and export handlers chmod 0600 after the write, and compressDirectory() walks the whole application tree first. A world-traversable parent is what makes that window reachable. 0750 is also what the application itself would have chosen — DirectoryHandler::create() defaults to it, and never gets the chance because checkOrCreate() only creates a directory that is not already there. Verified on the container: at 0755 'nobody' can enter var/backup, at 0750 it cannot, and the login page still serves 200.
|
Closing in favour of fixing the root cause. Tightening That breaks The directory mode was never the defect anyway — it is what makes the defect reachable. The defect is that |
The entrypoint creates four runtime directories, chowns them to
www-data, and then chmods exactlyone of them. Measured on the running container:
var/backupholds the backup archives — the database dump andconfig.xml— and the XML export,which carries every account's encrypted secret and key, and when no export password was given the
name, login, URL and notes of every account in the clear.
Those files are restricted to their owner, but only once they are fully written:
ArchiveHandler::compressDirectory()andXmlExport::export()bothchmod 0600after thewrite completes, and
compressDirectory()walks the whole application tree first, so the window isnot instantaneous. A world-traversable parent directory is what makes that window reachable —
anybody else on the host can enter and read during it.
var/tempholds the intermediates on theway there.
This is also the mode the application itself would have chosen:
DirectoryHandler::create()defaults to 0750. It never gets the chance, because
checkOrCreate()only creates a directory thatis not already there, and the entrypoint has already made it.
Verified on the running container
and the application still serves (
HTTP 200on the login page), with both suites green.Not in scope
The chmod-after-write ordering in the archive and export handlers is the other half of this, and it
is worth fixing on its own —
FileBackupHandlersFactoryalready shows the right shape, openingdatabase.sqland restricting it before any write. This PR closes the directory, which is whatmakes the window reachable at all.