MicProtocol / docs /security.md
Benaah's picture
Upload folder using huggingface_hub
b17baae verified
|
Raw History Blame Contribute Delete
11 kB
# Security & Compliance
This doc covers the security model, threat surface, and regulatory compliance posture of MicProtocol. It complements [Webhooks](./webhooks.md) (per-webhook auth) and [Operations Runbook](./operations-runbook.md) (incident response).
## Authentication
- **Provider**: Better Auth β€” handles session management, OAuth (Google, Apple), password hashing, email verification, and device sessions.
- **Tokens**: Session cookie `better-auth.session_token` (httpOnly, SameSite=Lax). Verified by `BetterAuthGuard` on every protected endpoint via `BetterAuthService.getSessionFromHeaders()`.
- **Workspace scoping**: Resolved from the authenticated user's `WorkspaceMember` memberships; the `hasWorkspace` tRPC middleware enforces the required role.
- **Dev mode bypass**: If `DEV_AUTH_BYPASS_ENABLED=true` and `NODE_ENV=development`, the API accepts an `x-user-id` header containing a user UUID. Used by the synthetic agent runner and local dev. **Never expose this in production** β€” the auth guard rejects it unless both conditions are met.
- **Webhook secret**: Not applicable to Better Auth β€” user lifecycle events are handled via the `Session`/`Account`/`Verification` Prisma models directly.
- **OAuth flows**: Google Calendar (`GOOGLE_CLIENT_*`), Nylas (`NYLAS_CLIENT_*`) for Google/Outlook/Exchange, with explicit redirect URIs in `.env`.
## Authorization
- **Role hierarchy**: `Owner > Admin > Editor > Viewer`. Workspace-level role assignment via `WorkspaceMember.role`.
- **Scope**: Every domain query chains through `workspace β†’ podcast β†’ entity`. The synthetic agent suite explicitly tests cross-workspace isolation in [`scripts/synthetic-agents/scenarios/cross-workspace.ts`](../scripts/synthetic-agents/scenarios/cross-workspace.ts).
- **Guest portal**: signed-token auth (HMAC-SHA256 with a workspace-specific secret, 7-day expiry, granular scope). No tenant-level access from guest tokens.
## Transport Security
- **TLS 1.3** minimum on all production connections (enforced by Clerk, Resend, R2, Nylas; verify at your reverse proxy).
- **HSTS** with preload β€” set on the edge proxy serving web + guest portal.
- **WS auth**: WebSocket connections require a JWT in `Authorization` (Socket.io) or a JWT signed with `MIC_REALTIME_SHARED_SECRET` (mic-realtime).
- **Socket.io `/events` hardening**: handshake origins follow the same policy as HTTP CORS (`common/origin-policy.ts` β€” explicit origins + `*.ROOT_DOMAIN` subdomains), incoming message payloads are capped at 64 KB, `join:*` payloads are shape-validated, and per-socket join rate limiting (30/min) bounds DB lookups.
## Data Isolation
| Layer | Mechanism |
|---|---|
| Application | Every query filters `podcastId β†’ workspaceId`; no cross-workspace join possible from the API code path |
| Database | Row-Level Security (RLS) enabled on tenant-scoped tables (`workspaces`, `workspace_members`, `podcasts`, `diaries`, `workspace_branding_settings`, `workspace_settings`, `workspace_integrations`, `conversations`, `tasks`, `media_comments`). Policies scope rows to `current_setting('app.workspace_id', true)::uuid`. Set via `RLSMiddleware` on each authenticated request. Migration: `20261023000001_enable_rls`. |
| Object storage | Per-tenant prefixes in the shared `micprotocol-storage` bucket; presigned URLs with 15-minute expiry |
The `workspace β†’ podcast β†’ guest` hierarchy is the foundation of complete data isolation. See [Database Schema β†’ Row-Level Security](./database-schema.md#row-level-security-rls-policy).
## Secrets Management
- **Single source of truth**: `.env` (never committed; `.gitignore` excludes it).
- **Generation**: `bash scripts/generate-secrets.sh` produces strong random values for every required secret.
- **Rotation policy**:
- **Never rotate after initial setup**: every `*_ENCRYPTION_KEY`, `*_NEXTAUTH_SECRET`, `CALCOM_ENCRYPTION_KEY`, `DOCUMENSO_ENCRYPTION_KEY/SECONDARY_KEY`, `FORMBRICKS_ENCRYPTION_KEY`, `FORMBRICKS_HUB_API_KEY`, `FORMBRICKS_CUBE_API_SECRET`. Rotation invalidates all encrypted data and sessions.
- **Rotatable on a schedule**: `*_WEBHOOK_SECRET`, `FORMBRICKS_CRON_SECRET`, `UMAMI_APP_SECRET`, Mongo/Redis passwords, SMTP credentials.
- **Rotatable via UI**: per-integration admin tokens (`CALCOM_API_KEY`, `FORMBRICKS_API_KEY`, `LISTMONK_API_TOKEN`, `CHATWOOT_API_KEY`).
- **Production**: store in AWS Secrets Manager or equivalent; inject at container start, never bake into images.
## File Uploads
| Concern | Implementation |
|---|---|
| Access | Presigned R2/MinIO URLs, 15-minute expiry, single-use |
| MIME verification | `Content-Type` checked against allowlist on ingest |
| Virus scanning | ClamAV scan on ingest (see deployment architecture doc) |
| Size limits | Enforced at the proxy and in the upload handler |
| Storage class | R2 Standard by default; lifecycle rules for archival if needed |
See [Architecture β†’ Data Security](./design.md#91-data-security).
## Rate Limiting
Redis-backed rate limiter with two tiers:
| Scope | Limit | Configurable via |
|---|---|---|
| Per IP (anonymous + guest portal) | 100 req/min | `RATE_LIMIT_TTL`, `RATE_LIMIT_MAX` |
| Per authenticated workspace | 1000 req/min | tRPC rate-limit middleware |
| Per WebSocket | 120 messages/min | `MIC_REALTIME_RATE_LIMIT` |
429 responses include a `Retry-After` header. Synthetic agents surface rate-limit gaps in [`weaknesses.json`](../scripts/synthetic-agents/weaknesses.json).
## Logging & Audit
- **Audit log**: every write operation emits an audit event to an append-only `audit_logs` table (per SOC2 CC6). Implemented via `AuditService` (best-effort writes, never blocks the request path). Migration: `20261023000000_add_audit_log`.
- **Trace propagation**: `X-Trace-Id` (`tr-xxxxxxxx`) crosses services and clients; `X-Response-Time` carries duration.
- **Telemetry endpoints**: `/tracing/spans` (recent spans + p95), `/tracing/metrics` (counts, error rate). Surfaced in the Admin app at `/monitoring` and `/analytics`.
- **No PII in logs**: the logger redacts known PII fields (email, phone) before emitting.
- **Integrations**: optional Sentry (`SENTRY_DSN`) and LogRocket (`LOGROCKET_APP_ID`) for error tracking and session replay.
## Privacy & Compliance
MicProtocol is built to comply with **GDPR (EU)**, **POPIA (South Africa)**, **Kenya DPA 2019**, and **Nigeria NDPR**. Features supporting these regimes:
| Requirement | Implementation |
|---|---|
| Right to erasure | Cascade workflow that anonymises guest PII across episodes, messages, and assets while preserving aggregate analytics |
| Data minimisation | Only fields required for outreach and booking are collected; open-ended custom fields lock behind workspace admin approval |
| Consent tracking | Outreach sequences record processing basis (legitimate interest) and flag sequences where unsubscribe was received |
| Email compliance | CAN-SPAM footer injection, `List-Unsubscribe` headers, automatic unsubscribe on `.unsubscribe` event |
| Data residency | Workspace billing tier unlocks the ability to specify R2 storage region (incl. `Africa South / Johannesburg` edge) |
| Breach notification | Operational runbook covers incident communication (see [Operations Runbook](./operations-runbook.md)) |
## Mobile-Specific Security
- **Certificate pinning** on all API calls (configurable per environment).
- **Secure storage**: Keychain (iOS) / Keystore (Android) for tokens and secrets.
- **Biometric authentication** (FaceID/TouchID) opt-in.
- **Jailbreak/root detection** β€” refuses to run on compromised devices in production builds.
- **Obfuscation** ready for production builds (EAS Build).
See [`apps/mobile/FEATURES.md`](../apps/mobile/FEATURES.md) for the full mobile security checklist.
## Webhook Security
Every inbound webhook uses `crypto.timingSafeEqual()` for HMAC signature verification β€” no timing side-channels. See [Webhooks β†’ Security Model](./webhooks.md#security-model) for the catalog and the per-provider checklist. LiveKit is the exception: it authenticates via a Bearer JWT (`iss` = `LIVEKIT_API_KEY`, HS256 with `LIVEKIT_API_SECRET`) whose `sha256` claim is verified over the raw request body.
Critical production safeguards:
1. Set every `*_WEBHOOK_SECRET` (empty values silently disable verification).
2. Sync secrets on both sides when rotating.
3. Restrict by source IP where the upstream publishes known egress ranges.
4. Monitor 401 rates from the webhook controller.
5. Never expose webhooks over plain HTTP.
## Vulnerability Detection
The synthetic agent framework ([`scripts/synthetic-agents/`](../scripts/synthetic-agents/)) continuously probes the API surface for known weakness categories:
- Auth guard gaps (missing role checks)
- Input validation failures (Zod schema mismatches, type coercion)
- Rate limit gaps (per-IP vs per-workspace)
- Subscription gating failures (paid features reachable on free tier)
- Privilege escalation (viewer β†’ admin)
- Workspace boundary leaks
Run a full sweep before any release:
```bash
pnpm agents:run
```
Findings categorised by severity in `scripts/synthetic-agents/weaknesses.json`. See the [Synthetic Agents Testing](./INDEX.md#synthetic-agents-testing) section of the docs index.
## Known Threats & Mitigations
| Threat | Mitigation |
|---|---|
| Token enumeration on guest portal | Per-IP rate limit (10 req/min) on `/portal/*` |
| Cross-workspace data leak | `workspaceId` filtering in every query handler; RLS at DB layer |
| Webhook replay | `WebhookIdempotencyService` dedupes via deterministic event keys |
| Route shadowing (duplicate endpoint) | `RouteCollisionService` fails API startup when two handlers register the same method + path |
| Subprocess injection (CLIP scorer) | `execFile` with argv (no shell); `assertSafeUrl` validates the video URL before download |
| Secrets in client bundles | Only `NEXT_PUBLIC_*` vars exposed to web; all others server-side only |
| CSRF on mutations | tRPC + SameSite cookies; Better Auth session cookie defaults |
| Server-side request forgery | `apps/api/src/common/safe-fetch.ts` validates URLs against an allowlist before fetch |
## Security Checklist for Releases
| Stage | Check |
|---|---|
| Pre-merge | `pnpm agents:run` passes; no new entries in `weaknesses.json` |
| Pre-deploy | All `*_WEBHOOK_SECRET` set; no `*_ENCRYPTION_KEY` is empty; `BETTER_AUTH_SECRET` set; `MIC_REALTIME_*_SECRET` set |
| Post-deploy | `/health/detailed` returns green; `/integrations/health` returns green for every integration in use |
| Quarterly | Rotate webhook secrets; review `audit_log` for anomalies; test backup restoration |
| Annually | Penetration test webhook + portal endpoints; review access logs for stale service accounts |
## Reporting Vulnerabilities
Internal reports: file a security-labeled issue in the repo with reproduction steps. External reports (researchers): contact the platform team via the security contact in the Admin app.