Security model
The billing-plane control catalog.
This page documents the security controls of the plane you are using right now: this website, signup, and billing. It is deliberately the least-trusted part of SanctiKey: it is public, it talks to the internet, and it is architecturally unable to reach your keys. Each control below says what it is and what it buys you, in plain language.
Control IDs here are local to the billing plane. The product plane, where your keys live, carries its own catalog of 97 numbered controls, 91 of them active, excerpted on the security model page. Both catalogs are self-attested: documented and tested by us, not yet third-party certified.
GH-01Webhook signature verification, before parsing
Every Stripe webhook is authenticated by an HMAC-SHA256 signature computed over the raw request body, checked with a constant-time comparison and a five-minute replay bound, before a single byte is parsed.
Why it matters: The webhook endpoint is public by necessity, so the signature is the authentication. Forged or tampered payloads are rejected without ever being interpreted; replayed captures die at the timestamp bound; constant-time comparison closes the timing oracle on the signing secret.
GH-02Card-data hard rejection (PCI SAQ-A by construction)
The signup endpoint rejects any payload containing card-like fields at two independent layers: a closed request schema at the gateway (unknown fields refused) and an explicit forbidden-field guard in the application.
Why it matters: Card data dies at the edge: it never reaches our compute, our logs, or our storage. Even if the edge schema were misconfigured, the application guard rejects card-shaped input with a dedicated error code, and the value is never echoed back.
GH-03Strict checkout input validation
Signup accepts exactly two fields, a tier from a closed enum and a pattern-bounded email, with a 2 KB body cap, validated at the gateway and again in the application from a single source of truth.
Why it matters: Injection probing, invented tiers, and oversized-body abuse are eliminated before our payment provider is ever called. The public API contract is asserted against the runtime allowlist at build time, so the two can never drift.
GH-04Webhook idempotency at two layers
Verified events are queued with deduplication keyed on the Stripe event id, and the billing ledger records each event with a conditional write on that same id.
Why it matters: Stripe delivers webhooks at-least-once. The queue absorbs redeliveries within its window; the conditional ledger write makes re-processing a permanent no-op. A replayed payment event can never double-record a billing fact.
GH-05Edge origin verification
Our CDN injects a long random secret header on every API request; the API gateway rejects requests that lack it. The secret is rotated by infrastructure code and never handled by humans.
Why it matters: Calling the API directly, skipping the WAF and rate limits, fails before any application code runs. The webhook path is exempt by design (Stripe cannot send custom headers) and is compensated by GH-01 plus the WAF.
GH-06Function privilege containment
Every serverless function runs under its own dedicated role, and every role is capped by a permission boundary that explicitly denies organization access, account enumeration, and role assumption.
Why it matters: The billing plane is public-facing, so it must be structurally unable to reach the provisioning plane. Even a fully compromised function cannot assume into another account or escalate: its ceiling is its own logs, its own queue, and its own secrets.
GH-07Sanitized error envelopes
Every externally visible error is a fixed code-plus-message envelope; unhandled exceptions are caught at every entry point and collapsed to a generic internal error.
Why it matters: No stack traces, cloud identifiers, exception text, or reflected input ever reach a client. Error codes are stable machine-readable strings, so clients can branch on them without the messages carrying internal detail.
GH-08Server-side identity resolution
The customer portal derives your identity exclusively from the verified token claim set by the gateway authorizer. Customer identifiers supplied in headers, query strings, or bodies are never read.
Why it matters: No code path reads identity from request input, so the insecure-direct-object-reference class is structurally absent: a portal user cannot view another customer by supplying a different identifier, because there is nowhere to supply it.
GH-09Edge hardening
A WAF with managed rule sets and per-IP rate limiting fronts everything; the site bucket is private and readable only by our CDN distribution; strict security headers (HSTS, frame-deny, nosniff) are injected on every response.
Why it matters: Generic exploit traffic and rate abuse are dropped before any origin. The site storage has zero public surface, and the security headers apply uniformly without depending on the origin to set them.
GH-10Payment secrets unreachable by CI
Stripe credentials live in a secrets vault as values pasted by a human; the application fetches them at runtime. The deployment pipeline’s secret permissions exclude reading or writing those values.
Why it matters: Payment credentials never appear in infrastructure state, CI logs, or environment variables. A compromised pipeline could rotate the harmless edge-verification header, but it cannot exfiltrate payment credentials.
GH-11Deployment trust boundary
Deploys use short-lived OIDC-federated identity, with no stored cloud keys. The read role serves pull requests; the write role trusts only the main branch behind a production gate, is fenced to billing-plane resources, and can only create roles that carry the privilege boundary.
Why it matters: A compromised pull request can read but not mutate. A compromised main-branch workflow is fenced to this plane’s resources and cannot mint an unbounded role: the classic pipeline privilege-escalation path is closed.
GH-12Privacy-aware observability
Access logs carry request metadata only (method, path, status, timing). Request bodies, signatures, emails, and card data are never logged anywhere; logs expire on a fixed retention schedule.
Why it matters: Observability without a PII shadow copy. Customer emails and payment metadata exist transiently in function memory and encrypted queues, never in log storage.
GH-13Pinned-artifact deploys
CI builds each function package, uploads it to a versioned artifact store, and infrastructure pins every function to the exact artifact version present at deploy time.
Why it matters: What was reviewed is what runs. A function’s code can only change through a CI-built, versioned, attributable artifact: stale code cannot silently survive a deploy, and out-of-band code changes have no path to production.
GH-14Supply-chain gates
Dependency hygiene is CI-enforced on both runtimes: a publish-age cooldown and disabled install scripts for JavaScript packages, vulnerability audits that fail the build on high severity, static security analysis for Python, and a drift gate proving the published API contract matches the code and the gateway wiring.
Why it matters: Freshly published trojan packages age out before they can be installed; install-time script execution, the dominant worm delivery vector, is disabled tree-wide; and the documented API can never quietly diverge from what is deployed.
Want to see these controls in motion? The billing plane walkthrough on the security model traces the same flows end to end.