Files
xamxam/docs/TODO.SECURITY.md
Théophile Gervreau-Mercier f5d3281c43 security: fix all LOW priority items from TODO.SECURITY.md
Item 13 — Remove deprecated X-XSS-Protection header
- nginx/posterg.conf: header removed (was '1; mode=block')
- nginx/SECURITY_HEADERS.md: new file documenting header decisions
  and explaining why X-XSS-Protection is counterproductive

Item 14 — Add rel="noreferrer" to external target="_blank" link
- public/admin/thanks.php: rel="noopener" → rel="noopener noreferrer"

Item 15 — Explicit (int) casts on all integer HTML outputs
- public/index.php: (int) on item id, page numbers
- public/search.php: (int) on totalItems, year options, item id, pagination

Item 16 — Remove unused DATABASE_PATH constant
- config/bootstrap.php: define('DATABASE_PATH', ...) removed

docs/TODO.SECURITY.md updated: items 13-16 marked resolved and
moved to the  Resolved section.
2026-02-08 13:54:02 +01:00

113 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. |
| 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 |
|---|-------|----------|-------|
| 6 | `.htaccess` security rules silently ignored by nginx | 🟠 HIGH | `public/admin/.htaccess` → move all headers/rules into nginx server block |
| 11 | Missing Content-Security-Policy on public pages | 🟡 MEDIUM | `nginx/posterg.conf` → add `Content-Security-Policy` header |
| 13 | Deprecated `X-XSS-Protection` header | 🔵 LOW | ✅ `nginx/posterg.conf` — header removed; documented in `nginx/SECURITY_HEADERS.md` |
---
### 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()` |
---
### File Upload / Storage
| # | Issue | Severity | Files |
|---|-------|----------|-------|
| 3 | Uploaded files stored inside the webroot | 🟠 HIGH | `public/admin/actions/formulaire.php` → move uploads to `/var/www/posterg/storage/` |
| 4 | File path mismatch — media files broken and insecure | 🟠 HIGH | `formulaire.php`, `memoire.php`, `search.php` → align stored paths with a controller-served endpoint |
| 9 | `error.log` written to a web-accessible path | 🟡 MEDIUM | `public/admin/actions/formulaire.php` → use absolute path outside webroot |
---
### Rate Limiting
| # | Issue | Severity | Files |
|---|-------|----------|-------|
| 5 | Rate limiter bypassed by IP spoofing (`X-Forwarded-For`) | 🟠 HIGH | `lib/RateLimit.php` → use `REMOTE_ADDR` only |
---
### 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. 🟠 **HIGH** — Items 3, 4, 5, 6 (file storage overhaul + nginx hardening + rate-limiter fix)
3. 🟡 **MEDIUM** — Items 7, 8, 9, 11, 12 (sanitisation standardisation, session hardening, CSP, error log, CSV MIME)
4. 🔵 **LOW** — ✅ All done (items 1316)
---
*Last updated: 2026-02-08*