Files
xamxam/docs/security.md
Pontoporeia 3cd96ed28a Deduplicate and standardise documentation
- Consolidate 36 markdown files → 14 (plus TODO.md)
- Merge overlapping docs into authoritative files:
  - database.md (from DATABASE_SPECIFICATION + QUICK_SCHEMA_REFERENCE + DATABASE_CONFIG + SETUP)
  - deployment.md (from SERVER_SETUP + COMPLETE_DEPLOYMENT_GUIDE + DEPLOYMENT_STEPS)
  - security.md (from SECURITY_ANALYSIS + TODO.SECURITY)
  - development.md (from DEVELOPMENT_GUIDE + LIVE_RELOAD_SETUP + TEST_CENTRALIZATION)
  - migration-history.md (from 11 past migration docs)
- Standardise all filenames to lowercase
- Remove non-doc files (Context.md research notes, chat export)
- Remove superseded docs (SECURITY.md pre-SQLite, SECURITY_IMPLEMENTATION, README_SECURE_SEARCH)
- Fix stale cross-references
2026-04-15 14:24:44 +02:00

3.9 KiB
Raw Blame History

Security

Vulnerability analysis and resolution status for posterg-website.

Based on security audit (2026-02-08). All items tracked below.


Resolved

Infrastructure / Deployment

# Issue Severity Resolution
1 No HTTPS — admin credentials exposed in transit 🔴 CRITICAL TLS terminated upstream by reverse proxy. nginx.conf doesn't need to handle TLS directly.
3 Uploaded files stored inside webroot 🟠 HIGH Storage moved to STORAGE_ROOT (/var/www/posterg/storage/), defined in config/bootstrap.php.
4 File path mismatch — media broken & insecure 🟠 HIGH DB paths now storage-relative. New public/media.php serves files safely. memoire.php and search.php use /media.php?path=…. Cover recording fixed.
5 Rate limiter bypassed by IP spoofing (X-Forwarded-For) 🟠 HIGH src/RateLimit.php getClientIdentifier() uses REMOTE_ADDR only.
6 .htaccess rules silently ignored by nginx 🟠 HIGH All rules ported to nginx/posterg.conf. See nginx/HTACCESS_TO_NGINX.md.
13 Deprecated X-XSS-Protection header 🔵 LOW Removed from nginx/posterg.conf.

Frontend / Assets

# Issue Severity Resolution
10 CDN stylesheet without SRI 🟡 MEDIUM CDN will not be used in production. Self-hosted, eliminating supply-chain risk.

Code Quality / Defence in Depth

# Issue Severity Resolution
14 Missing rel="noreferrer" on external links 🔵 LOW rel="noopener noreferrer" applied in public/admin/thanks.php.
15 Unescaped integer outputs 🔵 LOW Explicit (int) casts added in public/index.php and public/search.php.
16 Redundant DATABASE_PATH constant 🔵 LOW Removed from config/bootstrap.php.

Admin Panel — Authentication & Sessions

# Issue Severity Resolution
2 No PHP-level authentication in admin 🔴 CRITICAL src/AdminAuth.php implements session guard with password_verify + session_regenerate_id. All admin files call AdminAuth::requireLogin(). Credentials in gitignored config/admin_credentials.php. No-op when constant absent (dev/cli-server).
8 Session cookies not hardened 🟡 MEDIUM Resolved with #2. AdminAuth::startSession() sets HttpOnly=true, SameSite=Strict, Secure=true (off on cli-server), Path=/admin, Lifetime=0.

In Progress

# Issue Severity Status
7 LIKE wildcard injection in admin search 🟡 MEDIUM Public Database::searchTheses() escapes % and _ correctly. Same pattern must be applied to admin search and any other raw LIKE queries.

Not Yet Implemented

# Issue Severity Files
11 Missing Content-Security-Policy on public pages 🟡 MEDIUM nginx/posterg.conf → add CSP header to main server block
9 error.log in web-accessible path 🟡 MEDIUM public/admin/actions/formulaire.php → use absolute path outside webroot
12 CSV import missing server-side MIME validation 🟡 MEDIUM public/admin/import.php → add finfo MIME check

Priority Order

  1. 🔴 CRITICAL — All done (items 12)
  2. 🟡 MEDIUM — Items 7, 9, 11, 12 remaining
  3. 🔵 LOW — All done (items 1316)

Good Practices Already in Place

  • SQL injection: all queries use PDO prepared statements
  • XSS output: htmlspecialchars() on all user-controlled output
  • CSRF: tokens with bin2hex(random_bytes(32)), validated with hash_equals()
  • File upload: MIME type validated with finfo
  • Input validation: year, IDs, pagination cast to integers
  • LIKE wildcard escaping in public search (Database::escapeLikeString)

Last updated: 2026-02-08