Skip to main content

Security

:::info Source Sourced from services/catalog-service/SECURITY_MODEL.md in the documentation repo. :::

Companion: ../../docs/13-security-compliance-tenancy.md · DATA_MODEL

1. Threat Model

AssetThreatMitigation
Course metadataCross-tenant leakageRLS + visibility filter + X-Tenant-Id enforcement
Withdrawal reasonDisclosure of legal/privacy detailsReason stored but returned only to tenant admins
Taxonomy treeTamperOptimistic concurrency + audit
Admin endpointsPrivilege escalationPlatform-admin role + separate JWT audience
OutboxReplay leakageSigned envelopes, per-tenant ACL on NATS subjects
Change-log (sync)Cross-tenant driftService JWT aud=sync-service, tenant scoping in SQL

2. Authentication

  • Tenant JWT (RS256, issued by identity-service). Required claims: sub, tid, roles, exp, iat.
  • Service-to-service: JWT with aud=catalog-service or mTLS (in mesh).
  • Public API: no auth; tight rate limits; CDN layer.

3. Authorization (RBAC + ABAC)

Roles (platform-wide)

RoleScope
catalog.course.viewRead courses own tenant
catalog.course.editMetadata edits
catalog.course.visibilityChange visibility
catalog.course.archiveArchive
catalog.version.manageDeprecate / withdraw
catalog.taxonomy.editEdit tenant taxonomies
catalog.taxonomy.adminEdit global taxonomies (platform-admin only)
catalog.admin.replayDLQ replay

ABAC rules

  • Visibility check:
    deny if course.tenantId != caller.tid AND course.visibility NOT IN ('marketplace','public')
  • Write operations require caller.tid == course.tenantId.
  • Platform-admin role bypasses tenant filter for diagnostic endpoints only.

4. Input Validation

  • All inputs schema-validated (Zod) at API boundary.
  • String max lengths: slug 100, title.* 200, description.* 10,000, tag 50 (max 20 tags).
  • JSON depth cap 5; total body size ≤ 256 KB.
  • Taxonomy payloads capped at 1000 nodes / 100 KB.

5. Injection Defenses

  • SQL: parameterised queries via pg + query builder; no string concat.
  • NoSQL / JSONB: payloads stored verbatim; no expression evaluation.
  • Log injection: structured logging; \n/control chars stripped.
  • SSRF: no outbound HTTP to arbitrary URLs; media resolution goes through media-service client with allow-list.

6. Secrets Management

  • Secrets in AWS Secrets Manager or GCP Secret Manager.
  • Pulled at boot via Service Account.
  • No secrets in env var logs; DOTENV_DENY enabled.
  • Rotation: DB creds quarterly, NATS creds quarterly, signing keys annually.

7. Transport Security

  • TLS 1.3 required on all ingress.
  • Internal mesh: mTLS (Linkerd / Istio).
  • NATS: TLS + NKEY auth per service.
  • CDN: HSTS preload, CSP default-src 'self'.

8. RLS & Session Context

Every DB connection, immediately after checkout:

SET LOCAL app.current_tenant = $1;
SET LOCAL app.actor_id = $2;
SET LOCAL app.trace_id = $3;

Missing app.current_tenant → RLS denies all rows except where visibility is public/marketplace (which explicitly allows cross-tenant reads without tenant context).

Platform-admin context uses SET LOCAL app.is_platform_admin = 'true' and a policy clause OR current_setting('app.is_platform_admin', true) = 'true'.

9. Event Security

  • Envelope signed with Ed25519 per-service key; sig stored in envelope.sig.
  • Consumers verify signature for regulated events.
  • Subject-level NATS ACLs: catalog-service can publish only catalog.*; consume only content.play_package.built.v1, authoring.course_draft.published.v1, gdpr.*.

10. PII & Data Classification

FieldClass
authors[].displayNamePersonal (already public)
authors[].userIdPseudonymous ID
withdrawn_reasonInternal
all othersPublic (once visibility=public)

No special category data (health, biometric) in catalog.

11. GDPR & Data Subject Requests

  • Access: export JSON of all records referencing userId.
  • Rectification: update authors[].displayName per request.
  • Erasure: replace displayName with [redacted]. userId retained for audit unless legal basis forces deletion — in which case replaced with usr_00… tombstone.
  • Portability: not applicable (no user-owned content in catalog).

Handler: HandleGdprSubjectRequest use case; SLA 30 days.

12. Audit Logging

  • All write ops append to course_audit (immutable).
  • All read ops log traceId + tenantId + actor to central log (not to DB).
  • catalog.course.visibility_changed.v1 + audit row flagged audit=true for privileged-access review.

13. Rate Limiting & Abuse

  • Per-tenant and per-IP limits at API gateway.
  • Slowloris and body-size protections at edge.
  • Brute-force on /public/v1/courses mitigated by CDN and bot detection (Cloudflare / Fastly).

14. Supply Chain

  • Dependencies pinned via package-lock.json, checksummed.
  • Daily npm audit + Snyk; fail build on CVSS ≥ 7.0.
  • Container base image: node:20-alpine + distroless variant in prod.
  • SBOM generated on every build (CycloneDX).

15. Encryption at Rest

  • Postgres: AES-256 at storage layer, KMS CMK per region.
  • Redis: encrypted volume (EBS / GCP PD).
  • Backups: encrypted with separate KMS key; cross-region replicated.

16. Security SLOs

MetricTarget
Critical CVE patched≤ 48 h
Secret rotation window≤ quarterly
Audit log delivery≥ 99.99% within 1 h
Unauth attempts throttled100% (all fail-closed)

17. Compliance Touchpoints

  • SOC 2: change audit, access control, encryption.
  • GDPR: DSR handlers.
  • FERPA: catalog holds no student PII → minimal exposure.
  • ISO 27001: Annex A controls mapped via the global 13-security-compliance-tenancy doc.

18. Security Test Suite

TestFrequency
RLS bypass attempt (cross-tenant SELECT)every PR (integration)
AuthZ matrix (role × endpoint)nightly
SAST (Semgrep)every PR
DAST (ZAP)weekly on staging
Dependency scandaily
Penetration testannually