Security
What BankChangeProof does to protect accounts, vendor data, files and evidence, stated plainly.
Accounts
- Passwords are hashed with scrypt (N=32768, r=8, p=1) and a random 16-byte salt. They are never stored or logged in clear text.
- Passwords must be at least 10 characters, must not appear on a list of 1,000 common passwords, and must not contain your email address.
- Sessions are server-side and revocable. The cookie is HttpOnly, SameSite=Lax and, in production, Secure with the
__Host-prefix. Sessions expire after 30 days of inactivity and can be ended from Security. - Sign-in is limited to 10 attempts per minute per IP address and 20 per hour per email address.
- Email verification and password reset links are single-use, expire (24 hours and 30 minutes), and are stored only as hashes.
Tenancy
Every table that holds customer data carries the account id, and every query filters by it. Object ids are random and unguessable, but access never relies on that: a request for another account's object returns "not found".
Files
- Uploads are accepted only when their content matches an allowed type (PNG, JPEG, WebP, PDF, CSV, text, EML, XLSX), regardless of file name.
- Files are stored under generated names outside the web root, at most 10 MB each, and are served only through authenticated or tokenized routes. Non-image files are always delivered as downloads; uploaded HTML is never rendered.
- The SHA-256 of every file is recorded on arrival and printed in evidence PDFs.
Bank details
- Full bank account numbers are never accepted or stored. Last-four fields take exactly four digits and reject anything longer, and the database enforces the same rule.
- Free text (pasted emails, notes, comments, references) is scanned for runs of eight or more digits before it is saved; each run is replaced with a redaction marker that keeps only the last four digits, and the page reports how many runs were redacted.
- The phone number the app tells you to call is always the one on the vendor record, never one taken from a request. Numbers found in a request are shown only with a warning.
- Backups are plain SQLite files on the host's disk, readable only by the service user and root. Encryption of backups at rest is planned for v1.1 and is not in place today.
Records and evidence
- Events are chained per account with SHA-256; the chain can be verified at any time and a break is reported, not hidden.
- Evidence PDFs print the chain head at generation time and carry an integrity page with the SHA-256 of the document body. The file hash is recorded in the chain so it can be checked on the public verify page.
Transport and browser
- All traffic is served over HTTPS with HSTS. Requests arrive through Cloudflare.
- A strict Content Security Policy allows scripts and styles only from this origin, forbids inline scripts and framing, and restricts where forms may submit.
- Every state-changing request carries a per-session CSRF token and must originate from this site.
- Requests are rate limited (300 per minute per IP; 10 per minute on sign-in and token endpoints).
Operations
- Logs contain request ids, paths and timings. They never contain passwords, session ids, tokens or file contents.
- The database is backed up nightly; backups are kept for 14 days.
- Payment details are handled by Stripe through the InfiniHash App Store. BankChangeProof never sees card numbers.
Reporting a problem
If you believe you have found a security issue, email [email protected] with "security" in the subject. We acknowledge reports within two business days and do not pursue researchers who act in good faith.