Secrets and data protection
Keys, tokens, and personal data are what attackers are actually after. Protect them at rest, in transit, and in your source history.
Checklist
- Keep secrets out of the code and the client. API keys, database passwords, and signing secrets belong in environment variables or a secrets manager, never hardcoded and never sent to the browser. AI tools frequently inline keys into client code; grep your bundle for anything that looks like a key.
- Keep secrets out of git. Add
.env,*.pem, and*.keyto.gitignorebefore the first commit. Anything ever committed is compromised, even if later deleted, because it stays in history. Rotate it. - Encrypt sensitive data at rest. Use authenticated encryption (AES-256-GCM) for stored secrets and sensitive user content, with the key held outside the database.
- Serve everything over TLS. No plaintext HTTP for anything that carries a credential or personal data. Redirect HTTP to HTTPS and set HSTS.
- Do not put secrets or personal data in URLs. Query strings land in logs, browser history, and the Referer header. Put them in the body.
- Return the minimum. A profile or "me" endpoint should return only what the UI needs, never password hashes, keys, or internal flags.
- Store tokens where script cannot reach them when you can. A JWT in
localStorageis readable by any script, so an XSS steals it. An httpOnly cookie avoids that (at the cost of needing CSRF handling). Either way, a strict Content-Security-Policy is your backstop. See infrastructure. - Support data rights. Let users export and delete their data, and purge it on a schedule. This is both good practice and, for many, a legal requirement.
How to test
- Open the browser dev tools network tab and the JS bundle; search for
sk-,eyJ,password,secret,key. - Scan the repo and its history with a secret scanner.
- Hit your "me" endpoint and read every field it returns.