Security
Last updated 2026-07-29. A plain-English description of how we protect your account and the infrastructure we run on.
Encryption
All TOTP secrets are encrypted before being stored. The database holds ciphertext only; an attacker who reads a raw database row sees encrypted bytes, not a usable shared secret.
- Key management: the encryption key is held in the application's environment, never logged, never sent to the client, and never embedded in backups. The operator can rotate the key in place; existing rows are transparently re-encrypted so no data is lost during rotation.
- Recovery codes are stored as one-way hashes, not plaintext. Even the operator cannot recover them from the database; once consumed, they cannot be reverse-engineered.
Password hashing
Passwords are hashed using a memory-hard algorithm with parameters tuned to resist GPU and ASIC attacks. The same hardware that cracks MD5 in seconds does not make modern password-hashing schemes economical to attack at scale.
Hashes are salted; salting is handled by the underlying algorithm, so two users with the same password have different hashes on disk. We never store plaintext passwords, never email them back, and never log them.
Multi-factor authentication
2FA is mandatory for every account. There is no bypass for site administrators, no bypass for power users, and no way to disable it after enrollment. Every login requires both a password and a current time-based one-time password.
- Algorithm: industry-standard TOTP (time-based one-time password), compatible with Google Authenticator, Apple Passwords, 1Password, Bitwarden, and Authy.
- Rate limiting: login attempts are rate-limited per IP. After 10 consecutive failed logins the account is locked for 15 minutes. Lockouts release automatically; no manual operator intervention is required.
Recovery codes
Every account is issued 10 recovery codes at enrollment. Each code is 10 lowercase-alphanumeric characters (~1.7 × 1015 possibilities per code). They are shown once, at enrollment, and can be regenerated from your profile at any time.
- One-time use. A recovery code is consumed atomically in the same transaction as the successful login that used it.
- Stored as one-way hashes. Even the operator cannot recover a plaintext code from the database.
- If you lose both your authenticator AND your recovery codes, contact your organization administrator. For site-level assistance, the operator can issue a fresh recovery email — without ever knowing your password.
Infrastructure
The service runs on a single virtual machine hosted by Hetzner Cloud in Falkenstein, Germany. We do not use multi-region failover, container orchestrators, or third-party PaaS — the attack surface is the one Hetzner VM, plus the Cloudflare edge in front of it.
- Database: PostgreSQL, local to the VM. Local connections only; not reachable from outside.
- Outbound mail: DKIM-signed, with SPF / DMARC / MX records published in DNS. Inbound mail is delivered to Google Workspace; we do not run a mailbox on this server.
- Reverse proxy + TLS: Cloudflare terminates TLS for
crew2fa.comandwww.crew2fa.comusing an Origin Certificate valid 15 years. SSL mode is "Full (strict)". Cloudflare also provides bot management and edge caching. - SSH is not exposed publicly. The VM's firewall denies incoming on the public interfaces and only allows the operator's private management network plus the public HTTPS port. Port 22 is rejected at the kernel for non-management traffic.
- Process management: the application is supervised by a service manager that restarts on failure. Configuration and state live in separate directories and are both snapshotted nightly.
Responsible disclosure
We accept vulnerability reports from independent security researchers and operate under a coordinated-disclosure model. We will not pursue legal action against researchers who stay within scope, give us a reasonable window to fix before public disclosure, and avoid privacy violations or service disruption.
Severity tiers and fix SLAs:
- Critical — RCE, authentication bypass, encryption-key disclosure, mass data exposure. Fix within 7 days.
- High — stored XSS, CSRF bypass, privilege escalation. Fix within 30 days.
- Medium — information disclosure, denial-of-service, broken access control. Fix within 90 days.
- Low — cosmetic, style, documentation. Fix in the next sprint.
Reporter credit is included in the postmortem if requested. We do not currently run a paid bug-bounty program, but we will respond promptly, triage fairly, and disclose the fix publicly when the reporter agrees.
Vulnerability reporting
Where to send it: security@crew2fa.com. Every report is acknowledged within 2 business days and updated with a status within 7 days. We treat security reports with the same care as privacy incidents.
What to include:
- The affected URL or feature.
- The type of vulnerability (XSS, auth bypass, information disclosure, etc.).
- Step-by-step reproduction instructions.
- Observed vs expected behavior.
- Environment (browser, OS, your account email if you have one).
- Screenshots, logs, or proof-of-concept code where relevant.
What NOT to do:
- Do not run automated scanners against production. The Cloudflare edge will rate-limit you and the noise makes triage harder.
- Do not attempt to access other users' data, even if you find a way.
- Do not attempt denial-of-service, spam, or social engineering of our staff.
- Do not exfiltrate data beyond what is strictly needed to demonstrate the vulnerability.
Scope:
- In scope:
crew2fa.com,www.crew2fa.com, our public API (none at present). - Out of scope: third-party services we link to or integrate with (Cloudflare, Google AdSense, Hetzner — each has its own disclosure program); social engineering of our staff; physical attacks; UI/UX feedback (use bug_report@crew2fa.com for non-security bugs).
Backups
A nightly snapshot protects against operator error (accidental DROP TABLE,
silent corruption, bad migration) and against the unlikely event of an
infrastructure failure. It does not replicate off-host;
full regional failover is on the roadmap.
- What is backed up: a full database dump + a tarball of the application's configuration + a tarball of the application files (excluding build cache and virtualenv).
- Retention: 7 days rolling daily snapshots, plus 4 weeks of weekly snapshots.
- File permissions: backup files are
chmod 600; the backup directory is owned by root and not world-readable. - Restore tested monthly into a scratch database to verify that a backup can actually be recovered, not just written.
Audit logging
Every security-relevant action writes an audit log row with the actor, the organization (where applicable), the action name, the timestamp, the IP address, the user-agent, and a JSON metadata blob. Deleting a user preserves the audit row and nulls the actor pointer instead of cascading the trail away.
What gets logged:
- Signups, email verifications, and 2FA enrollments (including re-enrollments).
- Logins (success and failure).
- Logouts and password changes (self-service and admin-initiated).
- TOTP site creation, edits, and deletions (per organization).
- Invite issuance, redemption, and expiration.
- Membership changes (add / remove / promote / demote).
- Every admin action that mutates state.
- DMARC aggregate reports received.
Who can see it:
- Organization administrators see the last 200 rows for their organization.
- Site administrators see global audit history via the management interface.
- Regular members see only their own actions reflected in their profile (recovery-code regeneration, password changes, notification preferences).
Retention: for the life of the account plus 30 days of grace, after which associations are hard-deleted per the privacy policy.