Files
xamxam/docs/TODO.SECURITY.md
Théophile Gervreau-Mercier 0e4921583e refactor: reorganize to standard PHP structure
- Moved /lib → /src (PHP source code)
- Moved /includes → /public/includes (main site templates)
- Admin section remains self-contained in /public/admin with its own /inc
- Updated all require/include paths across codebase
- Updated config/bootstrap.php, justfile, tests, docs
- All tests passing 

Structure now follows PHP best practices:
  /config      - Configuration files
  /database    - SQLite database + schema
  /docs        - Documentation (intact)
  /nginx       - Server config (intact)
  /public      - Web-accessible files (entry point)
    /admin     - Self-contained admin interface
    /assets    - CSS, fonts, icons
    /includes  - Main site templates (header/footer)
  /scripts     - Deployment scripts (intact)
  /src         - PHP source classes (Database, AdminAuth, RateLimit)
  /tests       - Test suites
2026-02-12 12:11:16 +01:00

102 lines
4.9 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 | `src/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`. |
---
### Admin Panel — Authentication & Sessions
| # | Issue | Severity | Resolution |
|---|-------|----------|------------|
| 2 | No PHP-level authentication in admin panel | 🔴 CRITICAL | `src/AdminAuth.php` implements a session guard with `password_verify` + `session_regenerate_id`. All admin PHP files now call `AdminAuth::requireLogin()` instead of bare `session_start()`. Credentials stored in gitignored `config/admin_credentials.php` (define `ADMIN_PASSWORD_HASH`). No-op when constant is absent (dev / cli-server). Also resolves item #8 (session cookie hardening via `session_set_cookie_params` before `session_start`). See `nginx/PHP_AUTH_LAYER.md`. |
| 8 | Session cookies not hardened (`Secure`, `HttpOnly`, `SameSite` missing) | 🟡 MEDIUM | **Resolved as part of item #2.** `AdminAuth::startSession()` sets `HttpOnly=true`, `SameSite=Strict`, `Secure=true` (off on cli-server), `Path=/admin`, `Lifetime=0` before every `session_start()`. |
---
## 🔧 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 — 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** — ✅ All done (items 12)
2. 🟡 **MEDIUM** — Items 7, 9, 11, 12 (sanitisation standardisation, error log, CSP, CSV MIME) — item 8 resolved with item 2
3. 🔵 **LOW** — ✅ All done (items 1316)
---
*Last updated: 2026-02-08*