Control mapping
Implemented controls mapped to BSI IT-Grundschutz modules and OWASP ASVS chapters, each with an implementation pointer and an evidence source.
Note
How to read this mapping. References are chapter- and module-level, deliberately. Individual requirement identifiers are not cited: they change between editions of the IT-Grundschutz-Kompendium and between ASVS versions, and a fabricated requirement number is worse than no number at all. Confirm the mapping against the edition in force for your engagement.
A row means "this control contributes to that module", not "this module is satisfied". Most IT-Grundschutz modules extend well beyond what a TYPO3 extension can implement — see Target of evaluation.
Cryptography
BSI IT-Grundschutz: CON.1 (Kryptokonzept). OWASP ASVS v4: chapter 6 (Stored Cryptography), chapter 8 (Data Protection).
| Control | Implementation | Evidence source |
|---|---|---|
| Secrets encrypted at rest with authenticated encryption |
Encryption —
XChaCha20-Poly1305 or AES-256-GCM, identifier as associated data | tx_ contains no plaintext column;
Envelope scheme |
| Per-secret key separation (bounded blast radius) | One DEK per secret, wrapped by the master key
(encrypted_, dek_) | Schema; ADR-002: Envelope encryption |
| Master key never stored with the ciphertext |
Typo3Master,
File,
Environment
(and transit) | master setting;
Key custody |
| Domain-separated key derivation |
hash_ with three distinct info strings | HKDF usages |
| Recorded, not inferred, algorithm selection | encryption_ + encryption_ markers;
unknown marker is a hard error | Algorithm agility |
| Cryptographically secure randomness for keys and nonces |
random_; fresh nonce per operation and per re-wrap | Key and nonce lengths |
| Constant-time comparison of secrets and integrity tags |
hash_ in chain verification, anchor comparison and
token handling | Constant-time comparison |
| Minimised plaintext lifetime in memory |
sodium_ on success and in finally;
# | Memory scrubbing policy |
| Key rotation without plaintext exposure |
Encryption — DEK layer only | vault: output;
Key rotation |
Access control and authorization
BSI IT-Grundschutz: ORP.4 (Identitäts- und Berechtigungsmanagement). OWASP ASVS v4: chapter 2 (Authentication), chapter 4 (Access Control).
| Control | Implementation | Evidence source |
|---|---|---|
| Authentication required for every vault surface | TYPO3 backend authentication; frontend requests hold no operation permission |
Access;
Frontend and page-cache caveats |
| Least privilege by operation |
Vault — ten distinct permissions as TYPO3 custom
permission options | be_;
Operation permissions |
| Separation of machine consumption from human disclosure | secret. and secret., neither implying the other | Permission table; Verification procedures |
| Separation of duties for privileged operations | Distinct master_,
secret.,
vault.,
audit. / audit. | Group grants per backend group |
| Object-level authorization independent of operation permission | Per-secret owner / group tiers via
can / can / can | ADR-005: Access control |
| Server-side enforcement at every entry point | Operation permissions are enforced centrally in
Vault (store requires secret. /
secret. and — for access-policy changes —
secret.; rotate requires
secret.; delete requires secret.), so
DataHandler/FormEngine requests and programmatic callers face the
same gate as the module controllers. Controllers re-assert the
permissions as defense-in-depth.
Secret does so
too, and is the sole enforcement point for one case the
service cannot see: creating a tx_ record
without a value never calls store, so the hook asserts
secret. in
process and refuses the
record before it is inserted. |
Vault;
Secret; route
configuration plus controller code |
| No privileged short-circuit in the grant lookup | Grant evaluation deliberately avoids
Backend, which returns true
unconditionally for admins |
has |
| Administrative override is a single, withdrawable seam |
admin; disable in the
hardened profile, pinnable outside the backend | vault: reporting
admin |
| Emergency access is time-boxed, justified and evidenced |
Break — mandatory reason, TTL clamped to 1–60
minutes, audit written before the grant | break_ /
break_ rows;
Break-glass mode |
| Disabled accounts cannot act on a stale session | Defence-in-depth disable checks in
Access and
Break | Code review |
| Attributable impersonation for headless code |
Technical — validates the target user,
restores scope on exceptions, never mutates
$GLOBALS | actor_ audit rows;
ADR-029: Scoped technical-actor identity for headless use |
| Unattended CLI access closed by default | allow defaults to 0 | Configuration; Configuration |
| Frontend placeholder resolution restricted to published identifiers |
Frontend — a %vault placeholder
resolves only if the identifier was published from an admin-only
source (TypoScript setup, site configuration,
frontend, or an explicit
allow grant). frontend_ alone is no
longer sufficient, and the rule is strict on the CLI too unless
frontend opts out | ADR-035: Per-request allow-set of frontend-resolvable identifiers |
| Record copy and delete fail closed across vault fields |
Data — a record delete asserts every vault
field's delete gate before removing the first secret and is
cancelled outright if any fails; a copy that cannot clone every
secret deletes the ones it made and blanks every vault field. The
control is a preflight plus best-effort compensation, not
atomicity: a failure the preflight cannot predict leaves the
secrets already deleted unrestorable, a failed rollback delete
leaves an orphaned clone, and a failed blanking leaves the copy
still referencing the source record's identifiers. The record is
preserved either way, the delete and blanking residuals are named
to the editor, and every failure — the orphaned clone included —
is logged under a correlation reference | TCA integration; ADR-018: FlexForm secret lifecycle management |
| CLI grant narrowed to automation operations | cli defaults to
secret.; reveal, delete,
policy management, audit viewing, audit export, master-key
rotation and vault configuration must be added explicitly | vault: finding cli. |
Logging and audit
BSI IT-Grundschutz: OPS.1.1.5 (Protokollierung), DER.1 (Detektion von sicherheitsrelevanten Ereignissen). OWASP ASVS v4: chapter 7 (Error Handling and Logging).
| Control | Implementation | Evidence source |
|---|---|---|
| Every access, mutation and denial is logged |
Audit on read, create, update, rotate,
delete, metadata change and access_ | tx_;
ADR-006: Audit logging |
| Log entry precedes the effect where order matters | Break-glass audits before granting. The stock local
Vault store, rotate, delete and availability paths
commit the change and its audit entry in one database transaction
(ADR-050: Local mutation transactions).
Custom adapters use compensating rollback.
On the FormEngine/DataHandler paths, the
tx_ delete command runs through
Vault, and a metadata change (or
value-less record creation) whose audit write fails is reverted
by
Secret.
If that revert also fails, the change remains persisted without an
audit entry and the DataHandler log reports it for manual
reconciliation (Where the guarantee stops). |
Break,
Vault,
Secret |
| Complete attribution | actor_, actor_, actor_,
actor_, ip_, user_, request_ | Schema; bound into the hash from epoch 3 |
| Only known actions can enter the chain |
Audit rejects unknown actions loudly | Code review |
| Tamper detection on stored entries | HMAC-SHA256 hash chain; verification with
hash_ | vault:;
ADR-023: Audit hash chain HMAC consideration |
| Adversarial resistance against a database writer | Chain key derived from the master key, which the database does not contain | What the log proves — and against whom |
| Deletion detection | uid-sequence gap analysis → UID_ | Verification output |
| Algorithm-downgrade detection | Per-row epoch comparison, chain-level epoch floor, and the epoch bound into the hash from epoch 3 | EPOCH_ findings |
| Reset detection without external evidence |
Audit — MAC-signed tip in sys_
under a key derived from the master key, so truncating the audit
table alone leaves an anchor naming a row that is gone.
audit promotes a missing anchor from warning to
critical from a configuration file | vault: finding audit.;
vault: tip-anchor line;
ADR-034: Audit chain tip anchor |
| Reset detection through external evidence |
Chain — shrinkage, substitution and epoch
regression against a published anchor | TABLE_ findings;
anchor file / SIEM records |
| Log copies outside the protected store | Syslog (RFC 5424), append-only NDJSON, webhook — fan-out after commit, contained per sink | Sinks |
| Availability of the audit pipeline is observable | Persisted per-sink delivery state (last success / last failure /
consecutive failures, sys_) surfaced as
audit. findings; active end-to-end
verification via vault:
(audit.); process-local failure counters;
SINK_ and NO_ reason codes | Sink failure counters |
| Machine-readable alerting |
Audit with stable reason codes and
is | AuditIntegrityAlertEvent |
| Scheduled, unattended detection |
Audit,
Audit | Scheduler records; Scheduler tasks |
| No secrets in logs or exceptions | [REDACTED] placeholders; token redaction in transport errors;
response bodies not surfaced | Code review;
Secret |
Data protection and secret handling
BSI IT-Grundschutz: CON.1 (Kryptokonzept), CON.3 (Datensicherungskonzept). OWASP ASVS v4: chapter 8 (Data Protection).
| Control | Implementation | Evidence source |
|---|---|---|
| Disclosure responses are never cached | Cache- and Pragma: no- on every
reveal response, success and error alike |
Ajax; HTTP response inspection |
| Client-side exposure is bounded | start — 30 s auto-hide, wipe on
visibilitychange and pagehide | vault-;
Revealed plaintext in the browser cannot be zeroized |
| No client-side secret cache | Every reveal re-hits vault_, so every reveal is audited | Two reveals produce two audit rows |
| Clipboard exposure removed where it cannot be controlled | copy under the hardened profile; no copy button | Reveal response payload |
| Frontend disclosure is a property of the secret, not the visitor | frontend_; frontend requests hold no operation
permission | Frontend and page-cache caveats |
| Change detection without a plaintext oracle | value_ is a keyed MAC over the ciphertext, keyed
per secret from the DEK | The change-detection token |
| Recoverability of encrypted data | Documented separation of database and key-material backups, with a probe-decrypt verification step | Backup and restore |
| Detection of plaintext secrets elsewhere in the installation |
Secret, vault: | Scan output |
Configuration, fail-closed behaviour and supply chain
BSI IT-Grundschutz: CON.8 (Software-Entwicklung), OPS.1.1.5 (Protokollierung), DER.2.1 (Behandlung von Sicherheitsvorfällen). OWASP ASVS v4: chapter 10 (Malicious Code), chapter 14 (Configuration).
| Control | Implementation | Evidence source |
|---|---|---|
| Security policy is enforced in code, not documentation |
Security consulted by provider selection, access
control and audit verification | Exact technical differences |
| Fail closed on misconfiguration | Unknown profile → 1753900001; forbidden provider →
1753900002; no hardened fallback or auto-detection |
Master;
Verification procedures |
| Security-critical settings can be placed out of admin reach |
$GLOBALS pins | config/;
-- output |
| Deployment-time policy gate | vault:; exit 0 pass, 1
warnings, 2 critical | Pipeline logs and -- artefact |
| Outbound requests hardened against SSRF and DNS rebinding |
Secure; scheme restricted to http/https;
private targets need allowed_ | ADR-026: DNS-rebinding defence via CURLOPT_RESOLVE |
| Static analysis, security scanning and code review in CI | Shared security. (composer audit +
Opengrep SAST on registry rules, blocking on WARNING+),
codeql., license-,
dependency-, fuzz.. The in-repo
semgrep. is not referenced by any workflow — see
Development and release evidence | .github/;
Evidence collection |
| Secret scanning on every pull request | betterleaks job against the in-repo .gitleaks.,
reporting to GitHub code scanning | .github/ |
| Workflow-definition auditing | zizmor job over .github/, configured by
.github/ | .github/ |
| Broad compatibility test matrix | PHP 8.2–8.5 × TYPO3 ^13. and ^14., unit and functional,
coverage uploaded | .github/ |
| Release provenance | actions/ over the .zip and
.tar. — ungated. Requested at the call site with
id- + attestations: write | .github/; verify with
gh attestation verify |
| Release evidence is itself tamper-evident | actions/ over
release-, in the bundle job
with id- + attestations: write. The attestation
outlives the 90-day run-artifact retention | .github/; verify with
`gh attestation verify release-` |
| Software bill of materials | Two SBOMs per tagged release via anchore/:
<prefix>-<version>. (SPDX) and
.sbom. (CycloneDX). Delivered by the shared reusable;
include- defaults true and this repository's
release. does not opt out | Release assets *. / *. |
| Artefact signing | Keyless Sigstore signing via sigstore/ and
cosign sign-, over every file in dist/,
producing <file>. (that extension is chosen so
OpenSSF Scorecard recognises the artefacts as signed).
sign- defaults true; not opted out here | Release assets *.; verify with
cosign verify- |
| Artefact integrity | checksums. — sha256sum over the whole dist/
directory, including the SBOMs | Release asset; verify with sha256sum - |
| Third-party actions pinned by digest | Every third-party uses: in the release path is pinned to a full
commit SHA (sbom-, cosign-,
attest-, upload-). Netresearch-owned
reusables intentionally stay on @main so upstream fixes
propagate | Reusable workflow source; Development and release evidence |
| Continuous security posture measurement | OpenSSF Scorecard on schedule and on default-branch pushes | .github/; Scorecard results |
| Documented incident and emergency procedures | Runbooks for exposure, tampering and break-glass review | Incident response |
Declared gaps
Stated here so an assessment does not have to discover them, and does not credit them as controls.
| Gap | Status |
|---|---|
| No protection against a compromised PHP process | By design. Architecturally unfixable in-process; the alternative is recorded in ADR-016: Sidecar daemon option and is not implemented. |
| No HSM or cloud-KMS integration | Only indirectly, via HashiCorp Vault Transit. No provider exists for AWS KMS, Azure Key Vault or GCP KMS. |
| Audit chain is tamper-evident, not tamper-proof | By design. Detection only; prevention would require append-only external storage, which is an operator responsibility. |
| Metadata and the audit trail are unencrypted | Accepted. Identifiers, ownership, timestamps and the access history
are readable to anyone with database access;
audit. / audit. gate the application path only. |
vault: is a soft delete | Accepted. The ciphertext row remains until removed at the database level; crypto-erasure is the disposal mechanism — Decommissioning. |
| Revealed plaintext in the browser cannot be zeroized | Accepted, mitigated by a bounded exposure window and by disabling clipboard copy under the hardened profile. |
run is not an authentication boundary | Explicitly documented as such. Any extension code can act as any enabled backend user; the control provided is attribution, not prevention. |
Supply-chain controls are delivered by a shared reusable pinned
to @main, not by in-repo workflow steps | Accepted, by netresearch convention. SBOM generation, Cosign
signing and build-provenance attestation live in
netresearch/ (referenced at @main so
upstream fixes propagate), not in this repository's
release.. An assessment verifies them against the
resolved reusable and the produced release assets, not against
this repository's workflow files alone. |