Security
Splitly holds financial information about you and the people you share expenses with. This page describes what is actually implemented, not what we hope to do later.
What we never store
There is no screen anywhere in this product that asks for any of these. If something claiming to be Splitly asks you for one, it is not us.
- UPI PIN
- Card number
- CVV or card expiry
- Net-banking password
- OTP codes
- Bank account credentials
We do store a UPI ID if you add one. That is a public address, like an email address: it lets people pay you, and cannot be used to take money from you.
What is in place today
Access is checked on the server, every time
A group id sent by a browser is never trusted. Every read and write resolves the caller's membership row server-side first. If you are not a member, the group returns 'not found' rather than 'forbidden', so you cannot even confirm it exists.
Sign-in is run by a dedicated provider
Passwords, email verification codes, password resets and bot protection are handled by Clerk, an authentication provider, and passwords are only ever kept as one-way hashes. A new account is connected to groups you were added to by email only after Clerk has verified that the address is yours.
Receipts are private and time-limited
Receipt files are stored in a private bucket and are only ever served through signed URLs that expire in minutes. Uploaded files are validated by inspecting their actual bytes, not by trusting the file extension or the browser's declared content type.
Invite links are hashed and revocable
An invite token is generated from 32 bytes of cryptographic randomness. Only its SHA-256 hash is stored, so a database leak does not hand anyone working invite links. Tokens expire, can be limited to a single use, and can be revoked.
Every input is validated on the server
Client-side validation exists for a good experience only. Every request is re-validated on the server with a schema before anything is written, including checking that each person in a split actually belongs to that group.
Money never touches floating point
All amounts are stored and calculated as integer minor units — paise, cents, whole yen — with scaling done in BigInt. Splits use largest-remainder allocation, so shares always add back to exactly the bill total.
Payments are never assumed successful
Opening a UPI link tells us nothing about whether money moved, so we never treat it as payment. A balance clears when the recipient confirms it, or when a payment provider webhook with a verified HMAC signature reports success.
Rate limiting and audit logging
Invites, receipt scanning, payments and changes to groups are rate limited, and Clerk rate-limits sign-in attempts. Actions that move money or change access are written to an append-only audit log with the actor, the action, a timestamp and a hashed IP address.
Security headers
A Content Security Policy, HSTS, X-Content-Type-Options, a restrictive Permissions-Policy and frame denial are set on every response.
Being straight with you about what is not done yet
- Uploaded files are validated by type and size, but malware scanning is an integration point that is not yet connected to a scanner.
- Live payment provider integration is architected but has not been exercised against a production account, so settlements currently rely on human confirmation.
- We have not yet had an independent security audit or penetration test.
- Two-factor authentication is not implemented yet.
Found something?
Please tell us at support@splitly.example before disclosing it publicly, and give us a reasonable window to fix it. Please do not access other people's data while testing.