About · Security posture

How We Secure HobbyCardIndex

Composite 9.05 / 10 Last updated . Our April 2026 internal audit covers sixteen categories across authentication, authorization, transport, storage, and session management. This page summarizes what we scored and why, in plain language, for collectors who want to know what happens to their data.

Quick answer

HCI scored 9.05 out of 10 on our April 2026 internal security audit. Row Level Security covers 38 user-owned Postgres tables. Passwords use bcrypt plus a breach-database check. Admin routes sit behind a two-layer check. Two items are still on the pre-launch hardening list: full database transport certificate verification, and a planned migration of client session tokens from browser storage to HTTP-only cookies. Payment data is handled by Stripe and does not touch HCI servers.

The headline number

The composite is 9.05 out of 10, aggregated from sixteen categories at equal weighting. Our target for launch readiness is 9.5. The current posture is not yet where we want to ship, but it is substantially ahead of where it was in mid-April (prior composite 7.18). The gain came from wiring defense-in-depth primitives that were partially complete into coherent chains: Row Level Security, tiered admin isolation, per-user rate limiting, two-factor authentication with fail-closed semantics, and parameterized database access everywhere. The gap between 9.05 and 9.5 is not architectural. It is a short list of infrastructure hardening items listed in the roadmap section below.

This page is a plain-language summary, not a vulnerability disclosure. We reference findings at the category level only; no file paths, endpoint behaviors, or proof-of-concept code. The full attack plan is internal-only. If you believe you have found an issue, the responsible disclosure section below tells you how to reach us.

Category-by-category breakdown

Each category below covers what the control is, how we scored it, and what an average collector should take away. We use a 1 to 10 scale with equal weighting across categories. The individual numbers come from the April 2026 audit checklist.

Sixteen-category scorecard from the April 2026 internal audit. Composite is the average across all categories. Target composite for launch is 9.5.
CategoryScoreWhat it covers
Authentication9.2Password intake, login rate limits, session token issuance
Authorization (RBAC / RLS)9.3Admin and tier gates, Postgres Row Level Security across 38 tables
Input validation9.1Parameter validators and recursive body sanitization
CSRF8.9Double-submit cookie pattern with strict sameSite
CORS and origin9.2Exact hostname allowlist in production
Rate limiting9.3Global, auth-specific, and per-user DB-tracked limiters
Secret handling8.8Boot-time env validation, banned default rejection, HKDF-derived keys
SQL injection risk9.6Parameterized queries everywhere on the hot path
XSS surface9.0DOMPurify on rendered HTML, server-side sanitizer
Session management8.9Versioned JWT, instant invalidation on password change, 1-hour default expiry
Two-factor authentication9.1TOTP with fail-closed lockout on attempt-counter unavailability
OAuth (stubs only)7.5Social login not yet wired; callbacks return 404 pre-deploy
Admin isolation9.4Two-layer admin check (token claim plus DB re-verification)
Password policy9.310 character minimum, mixed case and number, banned list, HIBP check
File upload safety8.4Mimetype filter, 5 MB cap, 3-file limit, memory-only storage
Client token storage7.5JWT in browser storage today; migration to HTTP-only cookie queued
Composite9.05Equal-weight average across the sixteen categories

Authentication and session management

Passwords require ten characters minimum with mixed case and a number. We reject twenty common weak passwords and check every new password against HaveIBeenPwned via the k-anonymity API so we never see the password itself. Login is capped at ten attempts per fifteen minutes per IP; signup at five per hour per IP. Session tokens carry a version number we bump on password change or forced logout, which invalidates every outstanding token for that user immediately.

Authorization and Row Level Security

Thirty-eight user-owned database tables are protected by Postgres Row Level Security. Each table has three policies: the owning user can read and write their rows, an admin bypass for support, and a background-job policy for scheduled updates. The application sets a per-request user context that the database enforces. Admin routes use a two-layer check: a token claim verified against a fresh database lookup, with destructive endpoints going through a stricter DB-only check.

Input validation, XSS, and CSRF

Every route validates URL parameters against an explicit pattern (numeric IDs bounded to signed-integer range, string IDs to a safe character set). The request-body sanitizer recursively strips HTML tags, javascript-scheme URIs, and event-handler attributes. Client-side user-rendered HTML passes through DOMPurify. State-changing requests require a CSRF token sent both as a cookie (sameSite strict, 24-hour lifetime) and as a header, compared server-side.

Rate limiting and file uploads

Three rate-limit layers sit in series: a global IP limiter, auth-specific limiters on login and signup, and a per-user DB-tracked limiter that enforces tier-appropriate ceilings. Uploads are held in memory, capped at 5 MB per file and 3 files per request, and MIME-filtered to JPEG, PNG, and WebP. The audit flagged that MIME type is client-spoofable, which is why the pre-launch roadmap includes magic-byte verification plus a ClamAV antivirus sidecar.

Secrets, transport, and logging

On boot, the application validates every required secret against minimum length and a banned-default list. If any secret is missing, short, or matches a known default, the application refuses to start in production. Encryption keys for stored OAuth tokens are derived via HKDF from a master key that lives in an environment variable, not in the database. Database connections use TLS today; full chain certificate verification against the managed Postgres certificate authority is on the pre-launch list. Structured logs carry a correlation ID through every request with automatic redaction of sensitive headers.

What we use internally vs what ships to users

A security posture is the set of controls that actually protect a user, not the set of controls that exist somewhere. The table below separates what runs in the production path from what is internal-only tooling, so that the scorecard above is grounded in the user-facing surface.

Production user-facing stack vs internal operator tooling. The user-facing column is what covers a collector; the operator column is what helps us keep the user-facing column honest.
LayerUser-facing productionInternal operator tooling
AuthBcrypt + HIBP + banned list + 10-char minimum + rate limitsAdmin-force-logout, version-bump session invalidation
AuthorizationPostgres RLS on 38 tables + tier gates on metered featuresTwo-layer admin check with DB re-verification
PaymentStripe Checkout redirect; no card data on HCI serversStripe webhook reconciliation, customer-ID lookup
Data at restBcrypt password hashes, HKDF-derived encryption keys for OAuth tokensDB backup retention, planned offsite copies
Data in transitHTTPS everywhere with HSTS, sameSite strict cookiesFull-chain DB SSL verification (pre-launch roadmap item)
MonitoringPino structured logs with header redactionSentry error tracking, correlation IDs, OpenTelemetry spans

Payments deserve a specific call-out because it is the item collectors ask about most often. HCI does not store credit card numbers, CVVs, or full billing details. The checkout flow is a redirect to Stripe Checkout, and what returns is a customer reference ID plus tier state via webhook. A breach of HCI would not expose payment card data because payment card data does not live here.

Responsible disclosure

If you believe you have found a security issue, email [email protected] with a description of the issue, the affected URL or endpoint, and reproduction steps if you have them. Please do not post details publicly before we have a chance to respond.

Scope covers hobbycardindex.com and its subdomains. We will acknowledge receipt within three business days and will keep the reporter informed as a fix ships. HCI is a small team and does not currently run a paid bug bounty, but with your consent we will credit responsible disclosures on this page and in our release notes. Out-of-scope items include social engineering, denial-of-service testing against production, and any action that would affect another user's data.

What is still on the roadmap before public launch

Four items are tracked against the 9.5 target. All four are scoped and on the sprint plan.

  • Database transport SSL verification. Upgrade the database client configuration from permissive verification to full chain verification against the managed Postgres certificate authority. Must land before the first external connection to the production database.
  • ClamAV antivirus sidecar on uploads. Run the contents of every uploaded image buffer through ClamAV before accepting it into the scan pipeline. Combined with magic-byte MIME-type verification on the buffer itself, this closes the "spoofed content-type" gap that today the static MIME filter cannot catch.
  • Secrets migration from plaintext .env to the hosting provider's encrypted environment variable store. Production secrets today live in a chmod-600 .env file on the host. The pre-launch step moves them into the provider's encrypted variable store so that a droplet snapshot alone would not expose them.
  • Row Level Security role confirmation. Postgres enforces RLS strictly only for non-superuser database roles. The pre-launch task confirms the production database role is non-superuser and applies FORCE ROW LEVEL SECURITY on the protected tables as a belt-and-suspenders measure.

A second tier of items is on the first-ninety-days list: client session tokens migrated from browser storage to HTTP-only cookies, a dedicated security-events table with alerting on anomalous authentication patterns, backup codes for two-factor authentication recovery, and offsite database backups with a ninety-day retention window. Real improvements, but not launch gates.

Bottom line for collectors

HCI is a small independent team that takes security seriously enough to run a formal internal audit, publish the score, and ship the hardening work on a calendar. The current 9.05 composite reflects a stack with defense-in-depth primitives in place across authentication, authorization, input validation, transport, and session management. The remaining work to reach 9.5 is scoped infrastructure hardening rather than architectural rework.

If you store a card collection on HCI, the controls that matter most to you are: Row Level Security at the database layer protecting your rows from other users, bcrypt plus HIBP on your password, instant token invalidation when you change your password, two-factor authentication available on every account and required for admin and dealer tiers, and Stripe handling all payment data so a breach here does not expose card numbers.

We publish the scorecard, the methodology, and the roadmap because the sports card data space historically has not. If you are evaluating HCI against another pricing platform, ask them for the equivalent writeup. If the response is "we take security seriously" without specifics, that is a useful signal. See also our independence pledge for the ownership and conflict-of-interest disclosures.