← Development workflow

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:

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:

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:

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:

  1. stop the affected operation and preserve logs/audit evidence;
  2. suspend affected tenants or disable the narrow feature through supported operational controls;
  3. rotate/revoke exposed credentials through the secret or identity workflow;
  4. determine the exact tenant, host, time, identity, and data scope without copying sensitive evidence into tickets; and
  5. 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.