# Security Audit Checklist – JobPortal

## Implemented

- [x] Password hashing with `password_hash` / `password_verify` (PASSWORD_DEFAULT)
- [x] PDO prepared statements everywhere (no string-concatenated SQL)
- [x] CSRF tokens on all state-changing POST forms
- [x] Session regeneration on login
- [x] Secure session cookie flags (httponly, samesite)
- [x] Login throttling (DB-backed attempts + lockout)
- [x] Role-based middleware (candidate / company / admin)
- [x] Ownership checks on jobs, applications, CVs, messages, payments
- [x] XSS output escaping via `e()` helper
- [x] File upload MIME + extension + size validation
- [x] CV and company documents stored outside easy public listing; sensitive paths denyable in Nginx
- [x] Jobs cannot go public without admin approval
- [x] Premium activation only after server-side payment completion
- [x] Soft deletes on critical entities
- [x] Security headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy
- [x] Admin audit log for moderation actions
- [x] Private candidate profiles excluded from company search
- [x] Sensitive profile fields not exposed on public/recruiter views

## Production hardening (ops)

- [ ] Force HTTPS and HSTS
- [ ] Set `APP_DEBUG=false` and never display stack traces
- [ ] Strong unique `APP_KEY`
- [ ] Restrict `storage/` and deny direct access to `cvs/` and `company_docs/`
- [ ] Rate-limit login and report endpoints at reverse proxy
- [ ] CAPTCHA on register/login if abuse appears
- [ ] Regular DB backups
- [ ] Keep PHP and OS packages updated
- [ ] Implement real payment webhooks (no trust of client confirmation alone)
- [ ] Review file upload virus scanning if available

## Access control notes

- Role is never taken from form input for authorization.
- Application status changes require company ownership of the job.
- Message threads require participant membership.
- Admin routes require `AdminMiddleware`.
