Skip to main content

Threat model

This model names the assets to protect and the controls this design requires. It is the security review checklist for implementation. It is not a procedure for attacking the system.

Assets​

AssetWhere it livesImpact if disclosed or changed
Customer identity and tokensHashed refresh tokens and short-lived access tokensAccount takeover.
USD paymentsStripe and the payments tableWrong charges or missed refunds.
PhotographsPrivate bucketPrivate images become public.
Gallery access decisiongalleries.access_statusA customer sees files the photographer did not approve.
Reward ledgerreward_ledgerDiscounts are forged or erased.
Administrator actionsaudit_logsA decision cannot be reconstructed.
Provider and storage secretsEnvironment or a secret managerForged checkouts or direct bucket access.

Actors​

ActorTrust
VisitorNone. Can call public routes.
CustomerAuthenticated for their own records.
PhotographerTrusted to decide bookings and access. Still authenticated, with MFA, and audited.
StripeTrusted for payment events only after the signature check.
OperatorTrusted to deploy and restore. Uses a separate database role.

Threats and required controls​

AreaThreatRequired control
AuthenticationStolen password databaseArgon2id hashes. No reversible passwords.
AuthenticationStolen refresh tokenRefresh token stored only as a hash. Rotation on use. Logout revocation.
AuthorizationCustomer reads another customer's galleryServer-side owner filter on every request.
Gallery approvalPublished gallery treated as publicAccess status checked on every view and download.
Image storageBucket listing or permanent public URLPrivate bucket. Signed URL lifetime of 5 minutes. Access checked before signing.
Image storageRevoked user keeps a URLAccepted until expiry. Short lifetime limits the window. New signatures stop at revocation.
PaymentsForged webhook marks an order paidVerify the Stripe signature before any write.
PaymentsDuplicate webhook double-credits a rewardUNIQUE(provider, event_id) in the same transaction as the state change.
PaymentsSuccess redirect treated as paymentOrder stays unpaid until a verified webhook or provider read.
APIMalformed amounts and idsValidate input. Prices come from service_packages.
APIStolen access token used from another client15-minute access-token lifetime. Refresh tokens are rotated and can be revoked.
APICredential stuffingRate limits on login, setup, reset, and checkout.
UploadsDisguised file contentMagic-byte checks, 50 MB limit, and worker processing before READY.
ReferralsSelf-referral or a second rewardCheck constraint, unique referred user, unique qualifying order, unique reward pair.
ReferralsTwo checkouts spend one balanceUser-row lock and one ledger transaction.
ReferralsHistory rewritten after a refundAppend-only ledger. Corrections are reversals.
DatabaseNetwork sniffing or an over-privileged appTLS on DATABASE_URL. The database user is not a superuser. Queries go through the generated Prisma Client. Backups encrypted at rest.
AdministrationUnattended administrator tokenMFA before the access token is issued. Audit log append-only.
SecretsSecrets committed to the repoEnvironment variables or a secret manager. Seed data contains no live secrets.

Residual risks to accept explicitly​

  • A signed URL works until it expires, including after revocation. The 5-minute lifetime is the chosen bound.
  • The photographer is a powerful actor. MFA and audit logs record decisions. They do not prevent a legitimate administrator from approving a gallery.
  • Email delivery can fail. The in-app notification and the delivery row remain, and the worker retries. A customer who never receives setup mail uses the password-recovery path after an operator confirms the address, or a new setup token is issued.
  • Webhook delay leaves an order unpaid after the customer has paid at the provider. Reconciliation reads the provider. It does not trust the browser.

Review gate​

Implementation of payments, gallery downloads, and reward redemption is complete for security purposes only when the acceptance criteria AC-6, AC-8, AC-12 through AC-16, and AC-19 through AC-24 pass in automated tests.