---
title: "Known limitations"
manual: "Secrets Vault"
version: "1.2"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-vault:security-known-limitations@1.2"
source: "Security/KnownLimitations.rst"
rendered: "2026-10-31T22:33:18+00:00"
---

# Known limitations {#security-known-limitations}

> [!WARNING]
> Read this page before deploying nr-vault, and read it again before
> telling anyone what the vault guarantees. Every limitation below is a
> property of the design, not a bug awaiting a fix. Several of them cannot
> be fixed inside a TYPO3 extension at all.

Nothing on this page is hedging. A control whose boundary is undocumented
gets trusted past its boundary, and that is how a hardening feature turns
into a liability.

## Mutation atomicity has an explicit integration boundary {#security-known-limitations-mutation-transactions}

The coordinated stock local `VaultService` mutation paths commit secret,
applicable ACL relations and audit together on one actual database connection.
Split table mappings and an already active caller transaction are rejected.
Only SQLite and MySQL/MariaDB are supported; any other platform is rejected
before a write (PostgreSQL could not write audit entries before either).
Database atomic commit, transactional tables and no dirty reads are assumed.

On MySQL/MariaDB, an audit insert that rolls back consumes its auto-increment
value. While the in-database anchor matches the current tip, the next entry
takes the following UID explicitly, so a rollback or a killed process leaves
no false gap. Because this trusts the in-database anchor, a replayed older
anchor combined with deleted tail rows leaves no gap either. Tail deletion
behind a replayed anchor is detected only by evidence outside the database:
schedule [vault:audit-anchor](https://docs.typo3.org/permalink/netresearch/nr-vault:command-audit-anchor@1.2).

Custom adapters, legacy direct constructions without the coordinator,
DataHandler/TCA writes outside the service, master-key rotation and read
counters have separate boundaries. Compensation alone cannot provide crash
atomicity. See [ADR-050: Local mutation transactions](https://docs.typo3.org/permalink/netresearch/nr-vault:adr-050-local-mutation-transactions@1.2).

The finite TLA+ model and database/process tests check this defined boundary.
They do not prove all PHP executions or the whole extension correct. External
logs and success events are published after commit; loss of that process can
prevent publication, and an event listener can throw after the database change
has already committed. An exception after commit is not evidence of rollback.

## A compromised PHP process can request every secret {#security-known-limitations-process}

**The limitation.** Arbitrary code execution in the TYPO3 PHP process — an
RCE, a malicious extension, a hostile Composer dependency, a compromised
deployment — defeats every control in this extension. The attacker is inside
the trust anchor: they can call `VaultService::retrieve()`, or load the
master key provider directly, and take whatever the process may legitimately
take.

**Why it cannot be fixed here.** nr-vault runs in that process. A control
implemented in the same address space as its attacker is not a control.

**What actually helps.** Moving decryption out of the process, so that a
compromise yields a request-rate-limited oracle instead of the key. That is a
different architecture, evaluated in [ADR-016: Sidecar daemon option](https://docs.typo3.org/permalink/netresearch/nr-vault:adr-016-sidecar-option@1.2), and it is
not what this extension is today.

**What still holds.** Attribution. A read performed by compromised code still
writes an audit row, and with a KMS every unwrap is logged by a system the
attacker does not control. Detection after the fact is a genuinely different
thing from prevention — just not a substitute for it.

## The `typo3` provider does not separate the vault from TYPO3 {#security-known-limitations-typo3-provider}

**The limitation.** The default provider derives the master key from
`$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']` with HKDF-SHA256.
The derivation is sound, but it changes nothing about custody: anyone who can
read [`config/system/settings.php`](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/Configuration/Typo3ConfVars/Index.html#file-project-config-system-settings-php) can derive the master key. That
includes every backup of the configuration directory, every developer with a
copy of the production settings, and every process running as the web-server
user.

Its strength also cannot exceed the strength of `encryptionKey` itself. The
provider refuses source keys shorter than 32 characters, but a weak-but-long
key still yields a weak master key.

**Consequence for backups.** A dump of the database *plus* the configuration
directory is a complete break. See [Backup and restore](https://docs.typo3.org/permalink/netresearch/nr-vault:operations-backup-and-restore@1.2).

**What to do.** Use `file`, `env` or `transit` for anything holding
production credentials. The hardened profile enforces this: `typo3` is
rejected outright with exception code `1753900002`, and there is no
auto-detection fallback that could quietly put you back on it.

## Revealed plaintext in the browser cannot be zeroized {#security-known-limitations-js}

**The limitation.** JavaScript strings are immutable and garbage-collected.
Setting `input.value = ''` removes the value from the DOM; it does not
remove the engine's copies from memory. There is no browser API that would.

**What is actually provided.** A bounded exposure window, not secure
deletion: 30 seconds of visibility, plus an immediate wipe when the tab is
hidden (`visibilitychange`) or the page goes away (`pagehide`). Nothing is
cached client-side, so a revealed value does not survive a modal close, and
the response is marked `Cache-Control: no-store`.

**The clipboard is worse.** Clipboard contents outlive the dialog, outlive the
page, and cannot be cleared reliably from JavaScript — they may also
synchronise to other devices. That is why the hardened profile disables the
copy button entirely rather than clearing the clipboard on a timer, which
would be theatre.

## The hash chain does not prevent a database reset {#security-known-limitations-chain}

**The limitation.** The HMAC chain proves that no row was altered *within* the
stored chain. It cannot prove that the chain is still the same chain. An
attacker with `DELETE` rights on `tx_nrvault_audit_log` can truncate it and
let the service build a fresh, internally consistent chain from uid 1 — the
chain itself cannot distinguish that from a genuinely young installation.

**What closes the gap, partially.** Two chain-tip anchors, at different
distances from the attacker.

The in-database anchor ([ADR-034: Audit chain tip anchor](https://docs.typo3.org/permalink/netresearch/nr-vault:adr-034-audit-chain-tip-anchor@1.2)) records "row
uid=A still exists with entry_hash=H" in `sys_registry`, MACed under a key
derived from the master key — which is not in the database. Truncating the
audit table alone therefore no longer looks like a young installation: the
anchor still names a row that is gone, and verification reports a violation.

The external anchor is published to a store the database owner does not
control at all, giving verification two facts to check that no database write
can reach: the chain cannot have shrunk, and the row at the anchored sequence
must still hash to the anchored tip.

**What remains.** Detection, not prevention — and each anchor only as far as
its storage is trustworthy. An attacker who also holds `DELETE` on
`sys_registry` can remove the in-database anchor, which then reads as "not
armed" rather than as a violation; [auditAnchorRequired](https://docs.typo3.org/permalink/netresearch/nr-vault:confval-ext-nrvault-auditanchorrequired@1.2)
is what closes that, turning a missing anchor into a critical finding a
database writer cannot silence. It lives in the extension configuration, so a
database-write attacker cannot reach it — but unlike `disableAdminOverride`
it accepts no `$TYPO3_CONF_VARS` pin, so a compromised administrator can
still clear it from the Settings module. An anchor
file on a host the attacker owns can likewise be truncated, so anchors shipped
off-host through syslog or a webhook are what makes the external property
real. And in every case: **tamper-evident, not tamper-proof.**

## A KMS protects custody, not runtime {#security-known-limitations-kms}

**The limitation.** With the `transit` provider the master key is unwrapped
by HashiCorp Vault on demand and only the wrapped ciphertext sits locally.
That is a genuine improvement in custody, rotation and auditability. It does
**not** stop a live attacker inside the request: the process holds a token it
may legitimately use, so it can call `decrypt` and obtain the master key
exactly as the vault does.

**What it buys, precisely.** Key custody and rotation move into Vault; every
unwrap is centrally audited and revocable, so pulling the token or the policy
locks the vault out immediately with no key file left to recover; and a stolen
database plus a stolen webroot is useless without Vault access.

**What it costs.** Availability. An unreachable Vault is an unreadable vault.

## Backups need the database **and** separately stored key material {#security-known-limitations-backup}

**The limitation.** A database backup alone cannot be restored into a working
vault — the ciphertext is undecryptable without the master key. A backup of
both, stored together, is a single artefact that contains everything.

**Both failure modes are real, and they pull in opposite directions.** Storing
key material with the dump destroys the protection; storing it nowhere
destroys the data. The only correct answer is: back up both, store them
separately, restore both, and verify the restore with a probe decrypt. See
[Backup and restore](https://docs.typo3.org/permalink/netresearch/nr-vault:operations-backup-and-restore@1.2).

## Master key loss is data loss {#security-known-limitations-key-loss}

**The limitation.** There is no recovery path, no escrow, no vendor-side
reset. If the master key is lost, every secret in the vault is permanently
unreadable. This is not a limitation to be engineered away — a vault with a
recovery backdoor is a vault with a backdoor.

The `typo3` provider makes this easier to trip over than it looks: rotating
TYPO3's `encryptionKey` changes the derived master key and orphans every
stored secret. Treat `encryptionKey` as vault key material for as long as
that provider is configured.

## Break-glass restores full admin power {#security-known-limitations-break-glass}

**The limitation.** While a break-glass window is open, an administrator has
exactly what they had before the override was disabled: every operation
permission, and read, write and delete on every secret. Break-glass prevents
nothing.

**Its value is evidence and time boxing** — a named actor, a mandatory typed
justification, a hash-chained audit row written *before* the window opens, a
PSR-14 event observers can alert on, a banner every operator sees, and an
expiry between 1 and 60 minutes that nobody has to remember, evaluated at read
time on every access-control decision.

**Two gaps worth naming.** A window that simply expires writes no audit row —
nothing runs at the moment it lapses, so reconstruct the closed interval from
the activation row's `expiresAt` context value. And an administrator with
filesystem access can unpin `disableAdminOverride` instead of using
break-glass at all; that path is visible in the filesystem, not in the audit
log.

Treat an activation as an incident to review, not as routine maintenance
([Incident response](https://docs.typo3.org/permalink/netresearch/nr-vault:operations-incident-response@1.2)).

## Metadata is not confidential {#security-known-limitations-metadata}

Identifiers, ownership, group tiers, timestamps, version counters and the
whole audit trail are stored unencrypted. The audit log is the sensitive one:
it maps who holds which credential and when they use it, which is a useful
target in its own right. That is why `audit.view` and `audit.export` are
separate permissions, and why an export — which leaves the tamper-evident
store behind, with no hash chain, no retention policy and no further access
control — is gated apart from viewing.

## Out of scope entirely {#security-known-limitations-scope}

nr-vault makes no claim about, and provides no control over:

-   the operating system, the web server, the PHP runtime and its extensions;
-   the database server, its access control, its backups and its replicas;
-   TYPO3 core authentication, session handling and CSRF (inherited, not
    provided);
-   the browser, the clipboard, screenshots, screen sharing and cameras;
-   the SIEM or log pipeline once evidence has left the process;
-   physical and network security, and the trustworthiness of every other
    installed TYPO3 extension.

[Target of evaluation](https://docs.typo3.org/permalink/netresearch/nr-vault:auditor-target-of-evaluation@1.2) states the same boundary in the form an
assessor needs.
