Files
xamxam/docs/TODO.SECURITY.md
Théophile Gervreau-Mercier a2b1ff5f41 security: fix all HIGH priority items from TODO.SECURITY.md
Items resolved:
- #3 (HIGH): Move file uploads outside webroot to STORAGE_ROOT (/var/www/posterg/storage).
  Uploads were previously stored in public/admin/actions/data/ which is web-accessible.
- #4 (HIGH): Align file paths and add media.php controller.
  DB paths are now storage-relative (theses/YEAR/ID/file, covers/file).
  New public/media.php serves files with path-traversal jail, MIME allow-list,
  and proper caching headers. memoire.php and search.php updated to use /media.php?path=.
  Also fixed: cover images were never recorded in thesis_files (broken INSERT).
- #5 (HIGH): RateLimit::getClientIdentifier() now uses REMOTE_ADDR only.
  HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP are attacker-controlled headers that
  allowed unlimited rate-limit bypass by rotating spoofed IPs.
- #6 (HIGH): Port public/admin/.htaccess security rules to nginx/posterg.conf.
  Apache .htaccess directives are silently ignored by nginx; none were active.
  CSP added to /admin/ location block, .log file denial added globally,
  autoindex off made explicit. Documented in nginx/HTACCESS_TO_NGINX.md.

Supporting changes:
- config/bootstrap.php: add STORAGE_ROOT constant
- nginx/SECURITY_HEADERS.md: updated to reflect admin CSP and pending public CSP
- docs/TODO.SECURITY.md: items #3-6 moved to resolved; priority order updated
2026-02-08 14:01:45 +01:00

102 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🔒 Security TODO — posterg-website
> Based on `docs/SECURITY_ANALYSIS.md` (2026-02-08).
> Tracks resolution status for all identified vulnerabilities.
---
## ✅ Resolved
### Infrastructure / Deployment
| # | Issue | Severity | Resolution |
|---|-------|----------|------------|
| 1 | No HTTPS — admin credentials exposed in transit | 🔴 CRITICAL | **TLS is terminated upstream by the reverse proxy** (outside nginx). nginx.conf does not need to handle TLS directly. |
| 3 | Uploaded files stored inside the webroot | 🟠 HIGH | Storage moved to `STORAGE_ROOT` (`/var/www/posterg/storage/`), defined in `config/bootstrap.php`. `formulaire.php` updated. |
| 4 | File path mismatch — media files broken and insecure | 🟠 HIGH | DB paths are now storage-relative (`theses/YEAR/ID/file`, `covers/file`). New controller `public/media.php` serves files safely. `memoire.php` and `search.php` updated to use `/media.php?path=…`. Cover recording in `formulaire.php` fixed (was never inserted into DB). |
| 5 | Rate limiter bypassed by IP spoofing (`X-Forwarded-For`) | 🟠 HIGH | `lib/RateLimit.php` `getClientIdentifier()` now uses `REMOTE_ADDR` only. |
| 6 | `.htaccess` security rules silently ignored by nginx | 🟠 HIGH | All rules ported to `nginx/posterg.conf` (CSP to `/admin/` block, `.log` denial, `autoindex off`). See `nginx/HTACCESS_TO_NGINX.md`. |
| 13 | Deprecated `X-XSS-Protection` header | 🔵 LOW | **Removed** from `nginx/posterg.conf`; see `nginx/SECURITY_HEADERS.md` for rationale. |
---
### Frontend / Assets
| # | Issue | Severity | Resolution |
|---|-------|----------|------------|
| 10 | CDN stylesheet (water.css) without Subresource Integrity | 🟡 MEDIUM | **CDN will not be used in production.** The stylesheet will be self-hosted, eliminating the supply-chain risk entirely. |
---
### Frontend
| # | Issue | Severity | Resolution |
|---|-------|----------|------------|
| 14 | Missing `rel="noreferrer"` on external `target="_blank"` links | 🔵 LOW | `rel="noopener noreferrer"` applied in `public/admin/thanks.php`. |
---
### Code Quality / Defence in Depth
| # | Issue | Severity | Resolution |
|---|-------|----------|------------|
| 15 | Unescaped integer outputs (inconsistency) | 🔵 LOW | Explicit `(int)` casts added in `public/index.php` and `public/search.php`. |
| 16 | Redundant `DATABASE_PATH` constant never used | 🔵 LOW | `define('DATABASE_PATH', …)` removed from `config/bootstrap.php`. |
---
## 🔧 In Progress
### Database / Input Handling
| # | Issue | Severity | Status |
|---|-------|----------|--------|
| 7 | LIKE wildcard injection in admin search (`public/admin/index.php`) | 🟡 MEDIUM | **Needs standardisation of DB input sanitisation across the whole codebase.** The public `Database::searchTheses()` already escapes `%` and `_` correctly; the same pattern must be applied to admin search and any other raw LIKE queries. |
---
## ❌ Not Yet Implemented
### Infrastructure / Deployment
| # | Issue | Severity | Files |
|---|-------|----------|-------|
| 11 | Missing Content-Security-Policy on public pages | 🟡 MEDIUM | `nginx/posterg.conf` → add `Content-Security-Policy` header to main server block (admin already has CSP via item #6) |
---
### Admin Panel — Authentication & Sessions
| # | Issue | Severity | Files / Notes |
|---|-------|----------|-------|
| 2 | No PHP-level authentication in admin panel | 🔴 CRITICAL | All `public/admin/*.php` files. The reverse proxy + nginx Basic Auth is the only auth layer — a single point of failure. CSRF ≠ authentication (CSRF only prevents cross-site forgery on already-open sessions; it does nothing against a direct unauthenticated request). A simple PHP session guard adds real defence-in-depth for ~20 lines using stdlib (`password_verify`, `session_regenerate_id`) with negligible added attack surface — the risk of **not** having it (proxy misconfiguration, bypass, local dev without proxy) outweighs the risk of adding it. |
| 8 | Session cookies not hardened (`Secure`, `HttpOnly`, `SameSite` missing) | 🟡 MEDIUM | All admin PHP files using `session_start()` |
---
### Admin Panel — Error Logging
| # | Issue | Severity | Files |
|---|-------|----------|-------|
| 9 | `error.log` written to a web-accessible path | 🟡 MEDIUM | `public/admin/actions/formulaire.php` → use absolute path outside webroot |
---
### File Import
| # | Issue | Severity | Files |
|---|-------|----------|-------|
| 12 | CSV import missing server-side MIME validation | 🟡 MEDIUM | `public/admin/import.php` → add `finfo` MIME check |
---
## Priority Order for Remaining Work
1. 🔴 **CRITICAL** — Item 2 (add simple PHP session auth guard)
2. 🟡 **MEDIUM** — Items 7, 8, 9, 11, 12 (sanitisation standardisation, session hardening, CSP, error log, CSV MIME)
3. 🔵 **LOW** — ✅ All done (items 1316)
---
*Last updated: 2026-02-08*