Zero Trust Architecture
Zero Trust Architecture
The bloqr-backend implements Zero Trust Architecture (ZTA) across every layer of the stack — from the Cloudflare edge to the Angular frontend. This document describes the threat model, architecture, and implementation details.
Core Principle
Never trust, always verify. Every request is verified at every layer regardless of origin — including internal service-to-service calls, queue messages, webhook payloads, and admin operations.
Threat Model
| Threat | Mitigation |
|---|---|
| Unauthorized API access | Clerk JWT + API key authentication on all write endpoints |
| Privilege escalation | 4-tier authorization (Anonymous → Free → Pro → Admin) with least-privilege |
| CSRF / cross-origin attacks | CORS origin allowlist on write/authenticated endpoints |
| Bot abuse | Cloudflare Turnstile human verification on compile endpoints |
| Admin impersonation | Dual-layer: X-Admin-Key + Cloudflare Access JWT |
| SQL injection | 100% parameterized D1 queries via .prepare().bind() |
| SSRF via proxy | RFC 1918 / localhost / metadata IP blocking on /proxy/fetch |
| XSS via API responses | Zod schema validation on all frontend API consumption |
| Credential theft | Clerk SDK for auth state — no tokens in localStorage |
| Rate abuse | 5-tier rate limiting keyed by authenticated user or IP |
| Secret leakage | Worker Secrets only; CI lint checks for secrets in [vars] |
| BOLA / IDOR | Resource queries scoped to authContext.userId; validated by API Shield Vulnerability Scanner |
| Client-side script injection / supply-chain compromise | CSP enforcement via contentSecurityPolicyMiddleware(); violations reported to POST /api/csp-report and persisted to D1; Page Shield script-inventory sync generates ABP block/allow rules — see Page Shield Integration |
Architecture Layers
Layer 1: Cloudflare Edge
Before the Worker executes, Cloudflare provides:
- Turnstile: Human verification on write endpoints (
/compile*,/validate,/workflow/*) - Cloudflare Access: JWT verification on
/admin/*and management routes - WAF: API Shield schema validation and bot score thresholds
- Bot Fight Mode bypass rule: Allows curl/programmatic clients to reach all
/api/*paths onapi.bloqr.dev(see below) - Rate Limiting: Edge-level rate limiting before Worker invocation
- API Shield Vulnerability Scanner: Stateful BOLA/logic-flaw detection using AI call graphs (beta) — see API Shield Vulnerability Scanner
WAF Bot Fight Mode Bypass Rule
Bot Fight Mode is enabled on api.bloqr.dev to block automated browser scrapers. However, legitimate programmatic clients (curl, SDKs, CI pipelines) that send API keys must be able to reach the API without being challenged. A WAF custom rule configured with Action: Skip → Bot Fight Mode exempts them.
The rule expression must cover all /api/* paths — not just /api/admin/*:
(http.host eq "api.bloqr.dev") and (http.request.uri.path wildcard "/api/*")| Field | Value |
|---|---|
| Rule name | Allow programmatic clients on API |
| Expression | (http.host eq "api.bloqr.dev") and (http.request.uri.path wildcard "/api/*") |
| Action | Skip → Bot Fight Mode |
| Zone | bloqr.dev → Security → WAF → Custom rules |
⚠️ Common misconfiguration: Scoping the rule to
/api/admin/*only will silently block all non-browser clients on every other API endpoint (/api/compile,/api/auth/*, etc.) with a403 Forbiddenandcf-mitigated: challengeresponse header. Ifcurlrequests return 403, check this rule first. See KB-007 for the full diagnosis.
Layer 2: Worker Request Handling
Every request entering worker/worker.ts follows this flow:
Request → CORS preflight check → Authentication → Authorization → Rate Limit → Handler- CORS:
handleCorsPreflight()for OPTIONS;getCorsHeaders()on all responses - Auth:
authenticateRequestUnified()producesIAuthContextwith user, tier, scopes - Auth gate:
requireAuth(authContext)blocks anonymous access on protected routes - Rate limit:
checkRateLimitTiered()enforces per-tier limits - Security telemetry:
AnalyticsService.trackSecurityEvent()on all failures
Layer 3: Data Validation
All trust boundaries use Zod runtime validation:
| Boundary | Schema |
|---|---|
| Clerk webhook payloads | ClerkWebhookEventSchema |
| Clerk JWT claims | ClerkJWTClaimsSchema |
| API key creation requests | CreateApiKeyRequestSchema |
| API key DB rows | ApiKeyRowSchema |
| User tier DB rows | UserTierRowSchema |
Layer 4: Data Storage
- D1 (SQLite): All queries use
.prepare().bind()— never string interpolation - KV: Scoped Worker bindings — no global credentials
- R2: User-scoped key prefixes (
clerk_user_id/...) prevent cross-user access
Layer 5: Angular Frontend
- Auth state: Managed via Clerk Angular SDK — never
localStorage - Route guards: Functional
CanActivateFnguards enforce auth requirements - HTTP interceptor: Automatically attaches Bearer token to all authenticated requests
- Response validation: Zod schemas validate all API responses before consumption
CORS Policy
| Endpoint Pattern | Policy | Rationale |
|---|---|---|
/api/version, /health, /metrics, config endpoints | * (public) | Read-only, no auth |
/compile*, /workflow/*, /queue/* | Origin allowlist | Authenticated write |
/rules, /api-keys (CRUD) | Origin allowlist | Authenticated data |
/admin/* | Origin allowlist | Admin-only |
The allowlist is centralized in worker/utils/cors.ts and configurable via the CORS_ALLOWED_ORIGINS environment variable.
Auth Tiers
| Tier | Rate Limit | Access |
|---|---|---|
| Anonymous | Lowest | Public read-only endpoints |
| Free | Standard | Compile, validate, basic API |
| Pro | Elevated | All features, higher limits |
| Admin | Highest | Admin endpoints, management |
Security Telemetry
All security events are emitted to Cloudflare Analytics Engine:
auth_failure— JWT/API key verification failedrate_limit— Rate limit exceededturnstile_rejection— Turnstile human verification failedcors_rejection— Origin not in allowlistcf_access_denial— CF Access JWT invalidsize_limit— Request body size exceeded
CI Enforcement
The zta-lint.yml workflow runs on every PR and checks for:
- Wildcard CORS (
Access-Control-Allow-Origin: *) outsidecors.ts - Unparameterized D1 queries (string interpolation in
.prepare()) - Secrets in
wrangler.toml [vars] - Auth tokens in
localStorage
The api-shield-scan.yml workflow runs on every PR touching the OpenAPI spec and checks for:
operationIdcoverage on all operations (required for scanner call graphs)security:annotations on resource endpointscloudflare-schema.yamldrift fromopenapi.yaml- Clean spec validation
Related Documentation
- ZTA Developer Guide — practical guide for contributors
- API Shield Vulnerability Scanner — BOLA detection and scanner setup
- Security Policy — vulnerability reporting
- Auth Configuration — Clerk integration setup