Skip to main content

Security

DriftWise is designed around defense in depth — no single control is the only thing standing between an attacker and your data. This page summarises the controls we run in production. Exact implementation details (file paths, library versions, private endpoints, threat-model specifics) live in internal documentation; what follows is the posture we expect any security-minded customer to scrutinise.

Summary

AreaControl
EdgeTLS-only, edge rate limiting on authentication paths; frontend hostname proxied through Cloudflare; sign-in served by the identity provider's own managed CDN
IdentityOIDC, SAML SSO (Team+, coming soon), role-based access control
API authenticationHMAC-peppered API keys with scopes
Tenant isolationDatabase Row-Level Security enforced with FORCE, dedicated least-privilege database role
Encryption in transitHTTPS everywhere, managed database configured to reject plaintext connections, HSTS with preload
Encryption at restCloud-provider-managed disk encryption, runtime secrets held encrypted in the cloud parameter store under per-account KMS keys, cloud credentials encrypted with an application-layer AES-256-GCM key, result bodies wrapped with AES-256-GCM and bound to tenant via AAD before storage
Application defensesSSRF allowlisting, LLM prompt-injection envelope, XSS sanitisation, strict CSP, request size limits
PlatformServerless compute for the application tier (the one long-lived host is a minimal NAT instance carrying only egress), a least-privilege application IAM role with each grant traced to a call site or documented rationale, keyless AWS/GCP authentication in CI and at runtime
Supply chainSAST, SCA, SBOM generation, vulnerability scanning on every PR
OperationsTamper-evident audit log with per-org SHA-256 hash chain, structured logs, distributed tracing, managed database point-in-time recovery

Network and transport

  • TLS-only in transit. All customer traffic reaches DriftWise over HTTPS. The managed database accepts encrypted connections only — plaintext handshakes are refused at the server.
  • Edge protection — split posture. The frontend (app.driftwise.ai) is proxied through Cloudflare — all legitimate frontend traffic arrives via the edge. Cloudflare provides TLS termination, HSTS, and rate limiting on the OAuth callback path for the frontend. The sign-in hostname (auth.driftwise.ai) is served directly by our identity provider's own managed CDN with its own certificate — the hosted sign-in flow does not pass through Cloudflare. The API surface (api.driftwise.ai) is intentionally exposed directly: API-key clients — CI/CD pipelines, the DriftWise Kubernetes operator, and inbound provider webhooks — need an unrestricted path. Its origin-side defenses (HTTPS, per-route rate limiting, API-key + OIDC auth, body-size caps, SSRF guards) are described elsewhere on this page.
  • Private database. The production database has no public IP — it is reachable only from inside the private network the application runs on.
  • HSTS with preload. Strict-Transport-Security is served with a long max-age, includeSubDomains, and preload so that standards-compliant browsers refuse to downgrade to HTTP.

Identity and access

  • OIDC sign-in. User authentication is handled through an identity broker that issues signed, short-lived JWTs. The backend validates tokens against the broker's public keys on every request.
  • SAML SSO (coming soon). Customers on the Team plan and above will be able to connect their own identity provider via any SAML 2.0–compliant service (Okta, Entra ID, Google Workspace, JumpCloud, and others), self-service. See SSO for current status.
  • Role-based access control. Each organization has owner, admin, member, and viewer roles. Security-sensitive mutations (billing, identity provider configuration, role reassignment) require OIDC-backed owner/admin — API keys cannot perform them regardless of scope.
  • API keys.
    • Keys are hashed with HMAC-SHA256 and a server-side pepper. Only the prefix is stored in plaintext for display; the full key is shown to the user exactly once.
    • Keys carry scopes (read / write) that are enforced at the middleware layer. Security-critical mutations — billing, identity provider configuration, role reassignment, API-key revocation — are blocked for all API keys regardless of scope and require a live OIDC session (see RBAC above).
    • Revocation requires a human. API keys cannot revoke other API keys — the DELETE /api/v2/orgs/:id/api-keys/:id endpoint accepts only OIDC-backed owner/admin identities. This closes a lateral-movement path where a compromised write-scoped key could rotate itself or lock out the real owner.
    • Revocations publish to a shared revocation store checked by every backend instance before trusting a cached key; a revoked key stops working within seconds, not within the cache TTL.

Tenant isolation

Tenant data is isolated at multiple independent layers — losing any one of them should not leak rows or objects.

Database tier (relational data)

  1. Application filter. Every repository query that touches a tenant table includes an explicit WHERE org_id = $N clause.
  2. Database Row-Level Security with FORCE. Every tenant table has an RLS policy keyed on a per-transaction session variable. FORCE ROW LEVEL SECURITY is applied so the table owner does not silently bypass the policy.
  3. Least-privilege database role. The backend connects as a role that owns nothing, has no direct privileges on tenant tables, and cannot inherit privileges from administrative roles. Every query enters a transaction-scoped role switch that carries the caller's org_id; a query that escapes that wrapper returns "permission denied" rather than silently running unscoped.

The combination means a missed WHERE clause no longer leaks data — RLS catches it. A compromised policy does not leak data either — the application filter still runs. And a raw-pool query that bypasses both still fails because the connecting role lacks privileges.

Object-storage tier (scan results and compliance packs)

Scan results, analysis responses, and compliance-pack bundles live in object storage rather than the relational database. They are isolated by three independent controls:

  1. Typed tenant boundary. The storage API accepts a distinct OrgID type (not a plain string). Call sites must perform an explicit conversion at the authenticated-tenant boundary, so every place tenant identity crosses into the storage layer is grep-visible and auditable.
  2. Tenant-scoped key layout. Every object lands under an <orgID>/ prefix. The backend validates orgID against an allowlist regex before composing the path, so attacker-controlled characters (/, .., control bytes) cannot escape the prefix.
  3. Per-tenant envelope encryption. Every object body is encrypted with AES-256-GCM. The tenant identifier and object key are bound to the ciphertext via GCM Additional Authenticated Data (AAD). A raw bucket read at the wrong prefix, a moved or renamed object, or a rewrite of the plaintext header all fail authentication — the server returns "not found" rather than leaking another tenant's content.

The envelope layer in particular means that an attacker who obtains raw read access to the bucket — a leaked bucket grant, a misissued IAM permission, a misconfigured replica — reads ciphertext bound to tenant-specific AAD and cannot decrypt it without also holding the application's envelope key.

Encryption

  • In transit. HTTPS for all client and internal traffic. The managed database is set to accept TLS-encrypted connections only.
  • At rest.
    • Customer data lives on cloud-provider-managed persistent storage with envelope encryption enabled by default.
    • Runtime secrets (application encryption keys, the API-key pepper, platform LLM credentials) are held in the cloud provider's parameter store as encrypted values under per-account KMS keys; the database master password is generated and held by the managed database service itself and never appears in configuration.
    • Cloud credentials (the AWS/Azure/GCP credentials you attach for scanning) are encrypted with AES-256-GCM using a separate application-layer key before being written to the database. A database-only leak does not expose them.
    • Scan results, analysis responses, and compliance-pack bundles stored in object storage are wrapped in an AES-256-GCM envelope whose ciphertext is bound to the tenant and object key via GCM Additional Authenticated Data. An attacker with raw read access to the bucket sees ciphertext that cannot be decrypted outside its intended tenant context — moving, renaming, or misattributing an object fails authentication. See Tenant isolation → Object-storage tier above for the full control set.
  • Backups. Production databases have automated backups and point-in-time recovery enabled.

Application defenses

  • SSRF protection. Every user-supplied URL (webhook endpoints, custom LLM endpoints, state-source URIs) is validated against a public-HTTPS allowlist before any request goes out. Private, loopback, link-local, CGNAT, and metadata-service addresses are rejected. Production boot refuses to start if this guard is bypassed — there is no per-request escape hatch.
  • LLM prompt-injection envelope. Untrusted content sent to an LLM (Terraform plan JSON, resource names, webhook bodies, etc.) is wrapped in a clearly-delimited envelope. The paired system prompt asserts the envelope contract so the model distinguishes trusted instructions from untrusted data.
  • LLM output is never auto-applied. Generated remediation and Terraform code are returned to a human for review. There is no path in the product where a model's response is automatically applied to infrastructure.
  • XSS sanitisation. Markdown rendered in the UI is sanitised with an allowlist sanitiser before being inserted into the DOM.
  • Strict Content Security Policy. The UI ships with a narrow CSP — no inline scripts, no eval, no object-src, no frame-src, with per-environment allowlists for network destinations. The CSP is generated at build time and covered by regression tests that guard against accidental loosening.
  • Request body size limits. All HTTP endpoints enforce a hard request-body size cap.
  • Rate limiting. Authentication paths and expensive endpoints are rate-limited at the edge (on Cloudflare-proxied hostnames) and at the origin. Origin rate limiting uses counters in a shared coordination store, so per-IP and per-user budgets are enforced across every backend instance rather than per-replica. Platform-LLM usage and plan analyses are separately budgeted via plan-tiered weekly and hourly quotas.
  • Inbound webhooks. Webhooks are authenticated with HMAC signatures and protected against replay by a delivery-dedupe table. Replayed bodies return 200 already_processed without re-running side effects.

Platform hardening

  • Serverless compute. The API runs on managed serverless functions and background workers run as managed container tasks — no node images or cluster control plane for us to patch or for an attacker to persist on, and application compute is recycled continuously by the platform. The one long-lived host is a minimal egress NAT instance that terminates no inbound traffic and runs no application code.
  • Least-privilege workload identity. The application workloads (API, scan worker, drift worker) share a single narrowly-scoped IAM role in which each permission grant is traced to a concrete call site in the code or a documented operational rationale — no wildcard admin grants.
  • Keyless cloud-API authentication. Workloads and the CI/CD pipeline authenticate to AWS and GCP via short-lived, federated credentials; no long-lived AWS/GCP keys exist in CI or at runtime. (CI holds one narrowly-scoped static token for publishing static assets to the CDN.)
  • Supply-chain-verified images. Production container images are normally built and promoted by the CI/CD pipeline; every CI build produces an SBOM and is scanned for known vulnerabilities before being promoted. Production SBOMs are archived to durable storage for provenance.

Supply chain

Every pull request runs, as blocking checks:

  • Dependency vulnerability scanning. Backend and frontend dependencies are checked against public vulnerability databases, and SBOMs are scanned against known CVEs on every build.
  • Static analysis. Bearer SAST runs against the full source tree on every pull request. The Go backend gets an additional layer via gosec (security-focused SAST) and staticcheck (correctness lint). Any high-severity finding blocks the merge.
  • SBOM generation. Software bills of materials are produced for backend and frontend on every build. Production releases archive their SBOM to durable storage.
  • Integration tests against a real database. Every PR that touches the backend runs the full integration suite against an ephemeral database container — exercising real row-level security, real schema migrations, and real tenant isolation.
  • End-to-end tests. A separate E2E suite drives the real server binary on every push to the main branch, and the deploy pipeline is gated on the backend E2E suite passing for the same commit it ships. A browser-automation UI suite also runs on every main push (monitored, but not a deploy blocker).
  • CI-built artifacts. The CI/CD pipeline is the standard build and promotion path for production container images. Deploys are gated on the backend E2E suite, the Bearer SAST scan, and the SBOM + vulnerability scan passing for the exact commit being shipped; the remaining checks above run and block PR merges.

Observability and auditability

  • Tamper-evident audit log. Security-sensitive mutations — API key create and revoke, cloud account changes, billing state changes, identity provider swaps, membership and role changes — write to an append-only audit log. Each row joins a per-organization SHA-256 hash chain: every row references the hash of its predecessor, so any retroactive mutation or deletion of past entries breaks the chain. Database-level RESTRICTIVE row-security policies forbid UPDATE and DELETE from the application role, so the chain also grows append-only under a compromised handler. Owners, admins, and auditors can independently verify the chain on demand via GET /api/v2/orgs/:id/audit-log/verify, which walks every row, recomputes each hash, and returns ok or broken with the first broken sequence number. Entries record the actor, action, target, and timestamp; secrets and raw credentials are never logged. See Audit Logs for the full event catalog, verify protocol, and auditor-handoff workflow.
  • Structured application logs. Every request is logged with a request ID, HTTP method, path, status, and latency.
  • Distributed tracing. HTTP handlers emit OpenTelemetry spans for end-to-end request tracing; downstream calls (database, LLM providers, cloud APIs) inherit the request context so their latency shows up on the same trace.
  • Platform audit logs. Key-management decrypt events, IAM changes, and database admin operations are captured by the cloud provider's audit logging.
  • Client-side PII redaction. In-app error displays and console output strip emails, UUIDs, JWTs, API keys, and cloud access key IDs. No session-replay or RUM pipeline sends browser telemetry to DriftWise.

Response and recovery

  • Point-in-time recovery. Production databases retain backups with PITR enabled.
  • Rollback playbook. Every deploy can be reverted via the pipeline without manual surgery.
  • Key rotation. The database master password is managed and rotated by the managed database service; runtime secrets are rotated by re-issuing the encrypted parameter values under the documented runbook. The API-key HMAC pepper and webhook-signing secrets are single-value today: rotating them is a customer-visible event (all API keys must be reissued, all outbound webhook subscribers must pick up the new secret). The rollout of a dual-secret verification path for zero-downtime pepper and webhook-secret rotation is tracked on our internal roadmap.

Responsible disclosure

Found something? Email [email protected]. We respond within one business day and will work with you to coordinate disclosure.

What's not on this page

This page describes production security. Items we are actively tracking but have not yet shipped — IAM-based database authentication, database mTLS, CSP violation reporting, image digest pinning — are managed on our internal roadmap. We'll move them to this page once they land.