Security hardening: Firestore/Storage rules, password hashing, XSS, deps#2
Security hardening: Firestore/Storage rules, password hashing, XSS, deps#2EdmundLimBoEn wants to merge 2 commits into
Conversation
Scope: harden the anonymous-client forum app in place. A full server-side auth migration (Firebase Auth) is intentionally deferred; see SECURITY_AUDIT.md for what is fixed vs mitigated vs deferred. Access control & data layer: - Add firestore.rules and storage.rules (default-deny + schema/type/length validation, hasOnly key allowlist, http(s)-only URL checks) and wire them into firebase.json. Honest comments note these give validation/abuse- resistance, not true per-user authZ (needs server identity). - Stop persisting plaintext passwords. Hash at rest with a per-user random salt (expo-crypto SHA-256). Legacy plaintext records are migrated to salted hashes on read/login; rules forbid a plaintext `password` key. - Remove the destructive full-collection delete-by-diff sync (one-call wipe primitive); remote writes are now additive/targeted. - Centralize the client-side admin allowlist and reserve those usernames at signup so they cannot be registered. - Add input length caps / URL-scheme validation in the data + create paths. Output sanitization (web XSS): - Add urlSafety.js and gate markdown links/images through a scheme allowlist (http/https/mailto) at both render sites, neutralizing javascript:/data:/ vbscript:/file: URLs. Add maxLength caps on text inputs. Dependencies: - npm audit fix for non-breaking advisories.
Replace the single-round SHA-256 password hash with PBKDF2-HMAC-SHA256
(150k iterations) via Web Crypto, which is available on the web deployment
target (react-native-web / Firebase Hosting) and Node. This sharply raises
the offline brute-force cost of any leaked hash from the world-readable
users collection. A single-round SHA-256 via expo-crypto remains only as a
labeled weak fallback where Web Crypto is unavailable.
- Hashes are self-describing ("pbkdf2$<iters>$<hex>" / "sha256$<hex>"); new
verifyPassword() re-derives with the stored algorithm + parameters, so
mixed runtimes and the fallback verify correctly.
- getUser uses verifyPassword instead of a raw string compare.
- Raise minimum password length 6 -> 10.
- SECURITY_AUDIT.md finding GeeksHacking#2 updated; residual (client-side verification
vs a world-readable doc, weak fallback) noted as subsumed by the deferred
server-auth migration.
Verified: PBKDF2 round-trips (correct accepts, wrong password/salt reject),
derive ~12ms at 150k iters; full app still bundles.
|
Follow-up: addressed an automated review finding on password hashing. Upgraded the password KDF from single-round SHA-256 to PBKDF2-HMAC-SHA256 (150k iterations) via Web Crypto (available on the web deployment target and Node), keeping single-round SHA-256 only as a labeled weak fallback where Web Crypto is unavailable. Hashes are now self-describing ( |
Summary
Security hardening pass on the Geek-Collab forum app. Scope is deliberately harden in place — a full server-side auth migration (Firebase Authentication) is intentionally deferred. Every finding is labeled fixed / mitigated / deferred in
SECURITY_AUDIT.md.A scan agent produced the audit; fixes were split across an auth/data-layer track and an output-sanitization track, then independently reviewed (which caught and fixed two additional issues, incl. a plaintext-cache leak).
What changed
Access control & data layer
firestore.rules,storage.rules, wired intofirebase.json). Previously there were none — every collection (including stored passwords) was world-readable and world-writable. New rules are default-deny with per-field type/enum/length validation, ahasOnlykey allowlist (rejects a plaintextpasswordkey), and http(s)-only URL checks.Output sanitization (web XSS)
react-native-markdown-display; on web a link/imagehreflikejavascript:/data:/vbscript:/file:could execute. AddedurlSafety.jsand gated links and images through a scheme allowlist (http/https/mailto) at both render sites. AddedmaxLengthcaps on text inputs.Dependencies
npm audit fixfor non-breaking advisories.Honest scope note
Because the app has no server-side identity, these rules provide schema validation and abuse-resistance, not true per-user authorization. Authoritative admin/ban/mute/moderation enforcement and full credential confidentiality require a server identity (Firebase Auth) and are deferred — called out in code comments and
SECURITY_AUDIT.md.Verification
App.js(esbuild, all imports resolve).hasOnlykey restriction).