ADR-030: Read-time resolution of site-configuration vault references
Table of contents
Status
Accepted
Date
2026-07-23
Context
Site configuration may reference secrets with the
%vault syntax (see
Site configuration). The extension originally
shipped an auto-registered event listener,
Site, that resolved those references on
Site and wrote the resolved array back onto the
event.
That eager path is unsafe because of how TYPO3 core loads site
configuration. Site
dispatches Site only on a cache miss and then
var_s the post-event array — the array the listener has just
filled with decrypted plaintext — into the core cache, which defaults to a
file-backed backend on disk. Every later load is a cache hit that returns the
pre-resolved array without re-dispatching the event. Two guarantees break:
- Encryption at rest. The decrypted secret is written verbatim into
var/in cleartext, so a filesystem read (backup, misconfigured permissions, a second tenant) recovers it without the master key, the database, or any vault access.cache/ code/ cache_ core - Per-reader access control.
Vaultruns itsService:: retrieve () can/ expiry / audit checks exactly once — in whichever context warms the cache (an authenticated admin request, or a CLI run). A secret that is notRead () frontend_is then served from the cache to anonymous frontend requests and to lower-privileged backend users, because the gate never runs again.accessible
The listener also called the processor without the Site object, so it
always took the global-identifier branch and never distinguished
site-scoped secrets.
Decision
Remove Site. Resolution of site-configuration
vault references is caller-driven and happens at read time, through
Site (the entry point already documented in the
class and exposed as a public service):
$site = $request->getAttribute('site');
$processor = GeneralUtility::makeInstance(SiteConfigurationVaultProcessor::class);
$config = $processor->processConfiguration($site->getConfiguration(), $site);
$apiKey = $config['settings']['payment']['apiKey'];
The cached site-configuration array keeps the literal %vault
placeholders; the plaintext exists only within the request that resolves it,
and can runs for the actual reader on every resolution. Passing the
$site object enables site-scoped identifiers
(site:<site).
Consequences
- Plaintext secrets are never persisted to the on-disk
corecache, and the access decision is enforced per reader rather than once per cache generation. - Behaviour change. Consumers that relied on transparent resolution —
reading
$site->getand receiving a decrypted value — must now callConfiguration () [...] processat the point of use. That transparent behaviour was the unsafe path; the explicit call is the supported one.Configuration () - The processor, its interface, and its public service registration are unchanged; only the eager listener and its tests are removed.
- TypoScript resolution (
Typo) is unaffected: it is gated onScript Vault Listener frontend_and documented as making a secret frontend-readable by design.accessible