Security: a password even behind the VPN
Sign-in screenA cash-flow log is financial data: balances, income, expenses. The live app therefore runs inside several independent layers of protection, and each layer has to hold on its own.
The four layers
- Segmented network. The app runs on its own server in an isolated network segment. It has no open port towards the public internet.
- VPN only. The service only accepts connections on its private VPN address; without the VPN it is not even visible.
- Own devices only. Only devices used by the owner can join the VPN, each with its own key.
- Password. The app has its own sign-in: the password is stored only as a bcrypt hash, the session is a signed HttpOnly cookie that expires 8 hours after sign-in, and after 5 failed attempts within 15 minutes sign-in is temporarily blocked. The password can be changed inside the app.
Why a password if it is only reachable over the VPN?
- Defence in depth: the layers do not replace each other, they catch each other's failures.
- A lost or stolen device may still have an active VPN profile; the expiring session and the password still protect in that case.
- If a network rule were ever changed by mistake, the app still would not open up by itself.
- Other self-hosted services also run on the VPN; of those, only someone who also knows the password can reach the financial data.
Access by other self-hosted apps
Two other self-hosted apps (a financial overview and a home display) each read one aggregated value from Cash-flow. They can only reach two dedicated, read-only endpoints, using a separate internal credential; if it does not match, the request is subject to the same sign-in check as any other page.
And the demo?
- The demo is public on purpose, runs on a separate server and has no connection whatsoever to the live app or its network.
- There is no password and no session: the sign-in screen is for show only.
- There is no database; every number is fictional and anchored to today. Buttons change at most the current page; everything resets on reload.
- It uses a single cookie to remember the chosen language. The "Hide amounts" setting is stored locally by the browser.