Security practices
KMP handles member identity, organizational roles, youth information, documents, and administrative workflows. Security depends on several layers working together: database-per-tenant isolation, fail-closed host resolution, authentication, policy authorization, CSRF/API controls, secret management, auditing, and safe operations.
This page is a development contract, not a certification or record of a past scan. Re-test the implementation and deployed configuration for every release.
Trust boundaries
| Boundary | Required behavior |
|---|---|
| Request host → tenant | Resolve only through central registry; unknown/unhealthy/inactive/behind fails closed |
| Platform → tenant data | Enter one explicit tenant connection scope; restore all process state |
| Identity → resource | Authenticate, authorize the action, and scope collection queries |
| Browser → web mutation | Same-site/secure session posture plus CakePHP CSRF token |
| API client → API | Tenant host plus service-principal token authentication and policy checks |
| App → secret | Secret-store reference; never plaintext configuration/logging |
| App → object storage | Tenant storage resolver and authorized metadata lookup |
| Operator → platform | Dedicated host, central identity/session, TOTP/lockout, and central audit |
Database isolation limits cross-kingdom impact; it does not replace permissions within a kingdom.
Tenant resolution and connection safety
TenantResolutionMiddleware runs before normal tenant authentication and
authorization. Platform metadata failure returns 503, an unknown host returns
404, and inactive or schema-behind tenants return 503. There is no fallback
tenant.
Never select a tenant from a query string, form value, member attribute,
forwarded arbitrary database name, or public ID. Background work must enter
TenantConnectionManager::withTenant() using trusted central metadata. Never
retain a table, connection, cache result, mail client, storage client, or open
transaction after the scope.
A security review for tenant-sensitive work must include an A → B → A same-process test. See Multi-tenant architecture.
Authentication domains
Tenant web users authenticate as tenant Members on that tenant’s host.
Platform operators authenticate separately as central platform users on an
allowlisted platform-admin host, with their own sessions, TOTP, lockout, and
recovery controls. Do not grant tenant access merely because someone is a
platform operator.
Service principals are tenant-owned API identities. Store only hashes of client secrets/tokens, show newly generated credentials once, support revocation and time-bounded roles, and audit management/use. API controllers require a service principal and still apply authorization policies.
Quick-login devices and impersonation are privileged authentication features. Impersonation must retain the original actor, log session and action activity, present an obvious UI state, apply its restrictions, and offer a reliable stop path. Never suppress logs or authorization because the effective member appears to have access.
Authorization
All protected controller actions must perform the appropriate CakePHP
Authorization check. Collection endpoints apply policy scopes before paging,
search, export, API serialization, or grid formatting. Use established branch
scope rules (global, branch only, branch and children) and seeded permission
constants rather than raw role IDs.
Check both positive and negative cases:
- anonymous versus authenticated;
- ordinary member versus authorized role;
- in-scope versus sibling/out-of-scope branch;
- direct URL/API request even when the UI hides the control;
- impersonated identities and restore locks; and
- same public/internal ID in a different tenant.
Public IDs make enumeration harder but do not authorize access. Escaping output does not authorize disclosure.
Sessions and cookies
Production tenant sessions should use HTTPS-only, HttpOnly, appropriate SameSite
cookies. SessionCookieConfig leaves the Domain attribute unset by default, so
the cookie is host-only; broadening KMP_SESSION_COOKIE_DOMAIN can allow
cross-host session sharing and requires an explicit tenancy/security review.
Use the project’s session timeout/renewal configuration. Do not put credentials, secret tokens, full entities, or durable workflow state in session data. Treat session stores and backups as sensitive.
CSRF, APIs, and request validation
CakePHP CSRF middleware protects normal web mutations. In production HTTPS mode, the CSRF cookie is Secure, HttpOnly, and SameSite Strict; local HTTP uses the configured relaxed development posture. API paths skip cookie CSRF because they use service-principal token authentication instead of cookie-authenticated mutations.
The TenantCsrfTokenScope class is an extension point and is not currently part
of the middleware token scheme. Do not describe tokens as cryptographically
bound to a tenant. Current isolation depends on tenant resolution preceding
CSRF/authentication plus host-only sessions.
Use CakePHP request data access, entity accessibility rules, validation, build rules, and explicit allowlists. Never mass-assign actor IDs, approval state, tenant metadata, storage locations, or policy fields from request data.
Browser defenses
Application middleware sets X-Content-Type-Options, frame/referrer controls,
HSTS in non-debug mode, and a Content Security Policy with explicit sources,
object-src 'none', a self base/form/ancestor policy, and production insecure
request upgrading. Treat every new CDN, iframe, map, analytics, or external
connection as a CSP and privacy change.
Escape untrusted output with CakePHP helpers. Do not solve CSP failures by adding broad wildcards or more inline/eval capability. Keep external links, HTML-rich email/template input, CSV export, and Markdown/JSON rendering within existing sanitization patterns.
Secrets and cryptography
Access secrets through SecretStoreInterface. Managed secrets are encrypted in
the platform database using separately sourced wrapping-key configuration; the
local file driver is restricted to development/test-style environments.
Never log or document:
- database passwords/URLs;
- security salts, API keys, tokens, or client secrets;
- tenant KEKs, backup DEKs, wrapped recovery material, or escrow shares;
- TOTP seeds or recovery codes; or
- plaintext backup contents.
Environment import is missing-only so a stale deployment value cannot overwrite a rotated database secret. Key rotation and escrow are audited operations, not normal configuration edits.
Documents, uploads, and downloads
Document metadata is tenant-owned and objects are routed through the tenant storage resolver. Do not construct a storage container/prefix from request data or expose a raw object key as authorization. Download services load authorized metadata, select the current tenant’s storage configuration, validate object state, and stream with safe response headers.
Validate upload size, declared and detected type, extension, filename, and domain ownership. Store outside executable web paths. Virus/content scanning and retention requirements must be implemented and verified before making a compliance claim.
Backup and restore
Managed tenant archives are gzip-compressed logical JSON encrypted before storage, with per-backup DEKs wrapped by a tenant KEK. A destructive managed restore requires a suspended tenant and explicit confirmation. The platform database uses a separate encrypted PostgreSQL dump path.
Checksums and encryption protect archive handling; restore verification and access control remain required. Never use a tenant archive with the platform restore path, download plaintext to routine logs/tmp, or reactivate a tenant until its schema and smoke checks are current.
Logging, telemetry, and rate limits
Logs and central job errors must be useful without containing member records, emails, request bodies, credentials, authorization headers, or secrets. Operational tenant metrics use bounded route names and hourly aggregates, not raw URLs/IDs. Central lifecycle, backup, secret, and operator actions use the platform audit path.
Public lookup helpers use RequestRateLimiter backed by tenant-aware cache.
Authentication, quick-login, recovery, API, and expensive search paths need
appropriate throttling and non-enumerating responses. A cache outage must not
turn a security control into cross-tenant state.
The configured platform audit WORM sink is disabled by default and the file sink is a local testing option. Do not claim immutable external audit retention until an approved cloud sink, immutability policy, failure behavior, monitoring, and recovery have been implemented and tested.
Dependency and code review
For security-sensitive changes:
- use Composer/npm lockfiles and review dependency advisories;
- do not casually change face-detection or authentication dependencies;
- avoid shell interpolation and deserialize only trusted formats;
- use parameterized ORM/SQL and quote only trusted identifiers;
- validate redirect targets and proxy/forwarded headers;
- test concurrency for approval, workflow, queue, and lifecycle state; and
- add audit assertions without asserting sensitive payloads.
Run targeted negative tests, PHPCS/PHPStan, JavaScript tests where applicable,
and the full verifier when practical. Coverage/mutation hardening is available
through bash bin/verify.sh --with-coverage=security and
--with-mutation=security.
Incident posture
If cross-tenant exposure, secret disclosure, authorization bypass, or restore corruption is suspected:
- stop the affected operation and preserve logs/audit evidence;
- suspend affected tenants or disable the narrow feature through supported operational controls;
- rotate/revoke exposed credentials through the secret or identity workflow;
- determine the exact tenant, host, time, identity, and data scope without copying sensitive evidence into tickets; and
- recover and verify through the documented backup/deployment process.
Do not delete audit records, silently rewrite migration history, or use an unverified restore to make the symptom disappear.