Short version: Your personal data is encrypted before it's stored in the database.
Even if someone gained access to the database, they would see only random encrypted bytes — not your name,
email, phone number, or CV content.
🔒 Encryption at Rest
All personally identifiable information (PII) is encrypted using AES-256-GCM
— the same authenticated encryption standard used by banks, governments, and major cloud providers.
Each field uses a unique encryption subkey derived via HKDF-SHA256 from a master
secret, so that compromising one field's encryption does not affect any other.
✓
Full name
✓
Email address
✓
Phone number
✓
City / Location
✓
Country
✓
LinkedIn URL
✓
Professional bio
✓
Skills list
✓
Sector
✓
Seniority level
✓
CV filename
📄 CV Files Encrypted
Your CV file is also encrypted before being written to disk — not just the database record.
Files are stored with random UUID filenames and are never accessible directly from a web URL.
Only authenticated recruiters can download a CV, and each download is logged.
- Stored in a web-inaccessible directory
- Encrypted with AES-256-GCM using a dedicated file subkey
- UUID filename — the original filename is stored separately in an encrypted field
- Access requires authenticated recruiter session + is audit-logged
🔐 Two-Factor Authentication
All accounts use a two-step login process — your password alone is never enough to gain access.
📱
Authenticator App (Recommended)
RFC 6238 TOTP — compatible with Google Authenticator, Authy, Microsoft Authenticator, 1Password, Bitwarden. Works offline. Generates a new code every 30 seconds.
📧
Email OTP (Default)
A 6-digit one-time code sent to your email, valid for 10 minutes. Used automatically until you set up an authenticator app.
Backup codes: When you set up an authenticator app, 8 single-use backup codes are generated.
These let you regain access if you lose your phone. Each code is bcrypt-hashed before storage —
the plaintext codes are shown once and never stored.
💻 Trusted Devices
You can optionally trust a device for 30 days, which skips the 2FA step
for that browser on that device. Trusted device tokens are stored as HMAC hashes alongside the
browser's user-agent signature — the raw token is never stored and only lives in your cookie.
Changing your password, enabling/disabling 2FA, or logging out from all devices immediately
revokes all trusted device tokens.
🚫 Brute-Force Protection
Account Lockout
After 5 failed login attempts, the account is locked for 15 minutes. All lockout events are logged.
Rate Limiting
Login, registration, 2FA verification, and password reset requests are rate-limited per browser session to prevent automated attacks.
Timing-Safe Comparison
Password and OTP verification use constant-time comparison (hash_equals) to prevent timing-based attacks that could reveal valid email addresses.
CSRF Protection
Every form submission includes a cryptographically random token that prevents cross-site request forgery attacks.
🍪 Session & Cookie Security
- Session ID is regenerated on login to prevent session fixation
- Sessions expire after 60 minutes of inactivity
- All cookies are
HttpOnly (not accessible to JavaScript), SameSite=Lax
- Cookies are marked
Secure when the site is served over HTTPS
- Candidate and recruiter sessions are completely separate (different cookie names and paths)
🔑 Password Storage
Passwords are never stored in plaintext. They are hashed using
bcrypt with a cost factor of 12 — a deliberately slow algorithm that makes
brute-force attacks computationally expensive even if a database were ever leaked.
Password reset tokens are single-use, expire after 24 hours,
and are invalidated immediately after use.
📋 Security Audit Log
Every security-relevant event is logged: logins, failed login attempts, account lockouts,
2FA setup/disable, backup code use, CV downloads, profile updates and imports.
Logs include a timestamp, IP address, and event detail.
Recruiter access to candidate CVs is logged per download — there is a full audit trail of who
accessed what and when.