Skip to content
All guides

Security

The security decisions that are baked into the kit, what they protect and what they do not.

Updated 2026-10-22

What the kit does for you

A starter kit removes the repetitive parts of the foundation. It does not make an application secure by itself, and this page is not a compliance claim.

  • Session tokens are hashed with SHA-256 before they are stored, rotated on a fixed interval and revoked as a lineage when a replay is detected.
  • Passwords are hashed with bcrypt. A password longer than the 72 bytes bcrypt consumes is rejected, so two passwords never collapse into the same credential.
  • A failed credential check counts against the account and locks it once the threshold is reached. The response is identical to the response of a wrong password.
  • The sign up, the password reset and the verification resend responses are the same whether or not the address is registered, so none of them can be used to enumerate accounts.
  • Every state changing route checks the origin of the request and requires a JSON body.
  • Verification and password reset tokens are single use, expire, and are consumed with a conditional write so two concurrent requests cannot both succeed.
  • Every protected route resolves the account from the session, and the repository layer repeats the ownership check.
  • The structured logs redact tokens, secrets, hashes, signatures and authorisation headers.

The payment path

Nothing is ever granted from a browser redirect. The subscription state is written only when a signed webhook is processed.

  • The webhook signature is an HMAC SHA-256 over webhook-id.webhook-timestamp.body, keyed with the base64 decoded webhook secret, and the comparison is constant time.
  • A payload older than five minutes is rejected, and the raw body is read as text and never parsed before the signature is verified.
  • The webhook identifier is the idempotency key. The ledger lives in the database, so a redelivery is skipped across isolates and a restart.
  • A subscription event that is older than the last applied event is ignored, so a redelivered or out of order event cannot put a cancelled account back into an active state.
  • An event that carries an unknown product identifier grants no plan, and the amount and the currency of a renewal are checked against the catalogue.

Rate limiting

The counters live in the database, so a limit survives an isolate eviction. The address of a request is read from a header the edge overwrites, and the account scoped limits cannot be bypassed by rotating a request header.

  • The sign in endpoint is limited by the address and by the account, and the account is locked after too many failed credential checks.
  • The password reset endpoint is limited by the account and by the address.
  • The bucket key is a hash, so it never contains an address in the clear.

What you still own

The kit cannot decide these for you. They are the parts of the security work that belong to the product and to the deployment.

  • The production secrets, the D1 binding and the custom domain.
  • The Dodo Payments account, the products, the prices and the webhook configuration.
  • The transactional email provider, the sender domain and the deliverability of the verification mail.
  • The legal pages, the terms, the privacy policy and the data you collect beyond the account.
  • The rate limit values, the lockout threshold and the session lifetimes, which are configurable through the environment.

Reporting a vulnerability

Report a suspected vulnerability to security@abackstack.com with the steps to reproduce it, the affected version and the impact you observed. Please do not open a public issue for a suspected vulnerability.