Architecture
Security Model
MCP Ambassador is designed with defense-in-depth. Every layer independently enforces security — authentication, authorization, encrypted credential storage, audit logging, and kill switches.
Security principles
- Zero trust — every request is authenticated and authorized, no implicit trust
- Least privilege — users get the minimum tool set required for their role
- Defense in depth — authentication, authorization, audit, rate limiting, kill switches
- Credential isolation — downstream MCP credentials never leave the server; clients never see them
- Encryption in transit — TLS for all client-server and server-MCP communication
- Encryption at rest — secrets encrypted in the backing store (AES-256-GCM)
- Audit everything — every tool call, every auth decision, every config change
Credential vault
Per-user credential storage uses AES-256-GCM with HKDF key derivation.
How it works:
- Server generates a master key on first boot (32 bytes, stored at
$DATA_DIR/credential_master_key, permissions 0600) - Each user gets a unique
vault_salt(32 bytes, stored in the users table) - When a user enters credentials for an MCP:
- User key =
HKDF(master_key, salt=vault_salt, info="mcpambassador-credential-vault-v1") - Encrypted =
AES-256-GCM(user_key, plaintext=credentials) - Stored in
user_credentialstable with IV
- User key =
- When spawning a per-user MCP:
- Decrypt credentials with user's derived key
- Inject into MCP process environment
- Zero memory after use
Threat model (Community tier):
| Scenario | Protected? |
|---|---|
| Stolen database alone | ✅ Cannot decrypt (needs master key + user salt) |
| Stolen master key alone | ✅ Cannot decrypt (needs database + salt) |
| Database + master key stolen | ⚠️ Full compromise (accepted for community tier) |
Session security
Sessions use HMAC-SHA256 signed tokens:
- Preshared key (
amb_pk_) — admin-issued, stored by developer in config - Session token (
amb_st_) — ephemeral, issued on registration, expires after idle timeout - Session secret — HMAC signing secret, persisted to survive server restarts
- Cookie security —
httpOnly,secure,sameSite: strictfor web UI sessions
Login rate limiting: 5 attempts per IP per 5 minutes.
Password security
User passwords are hashed with Argon2id:
- Argon2id is the recommended algorithm from OWASP and the Password Hashing Competition
- Minimum password strength: score 3+ (zxcvbn), 12+ characters
- Passwords compared using
crypto.timingSafeEqualto prevent timing attacks - Session ID regenerated on login to prevent session fixation
Transport security
TLS is always on:
- Development: Auto-generated self-signed certificates on first boot (TOFU model)
- Production: Configure CA-signed certificates via environment variables
- Client trust: Client receives server's CA fingerprint on first registration; must confirm trust
Content Security Policy
The React SPA enforces strict CSP headers:
- No inline scripts (all code in external bundles)
- Source maps removed from production build
- CSRF protection via
sameSite: strictcookies + double-submit cookie pattern
Security audit history
MCP Ambassador underwent multiple security reviews during development:
- ADR-001/003 review — Resolved findings F-001 through F-005 (timing attacks, session fixation)
- Architecture review — Resolved findings F-006 through F-016 (credential handling, key management)
- M26 credential vault review — Resolved race conditions, memory zeroing, env var injection
- Phase 3 review — Resolved CSP, source maps, dependency audit (0 vulnerabilities)
0.8.0-beta.1 ships with 327 passing tests covering authentication, RBAC, credential vault, and full subscription lifecycle.
Responsible disclosure
Found a security issue? Please report it via GitHub Issues with the security label, or contact the maintainers directly. Do not disclose publicly until a fix is available.