Madraset El Shamamsa, Part 5: Security Architecture and Legacy Hardening
Security on a long-running PHP platform is not a binary label. Madraset El Shamamsa has meaningful controls at the edge, web server, application, and data layers, but their coverage reflects different generations of the codebase. The responsible description is therefore: layered defenses with known modernization gaps.
Defense in depth
flowchart TB
Internet["Public internet"] --> Edge["Cloudflare proxy and TLS"]
Edge --> Apache["Apache and .htaccess controls"]
Apache --> PHP["PHP application"]
PHP --> CSP["Generated Content Security Policy"]
PHP --> Session["Session and authentication helpers"]
PHP --> CSRF["CSRF tokens on protected write flows"]
PHP --> Data["PDO prepared paths and legacy mysqli paths"]
Data --> DB[("MySQL")]
PHPConfig["PHP config loader (untracked file + env overrides)"] --> PHP
WorkerSecrets["Worker bindings and secrets"] --> Worker["Assistant Worker"]
PHP -->|"Server-side proxy request"| Worker
Worker -->|"Response"| PHP
Cloudflare provides the public proxy and TLS boundary. Apache adds transport and browser-facing headers, disables directory indexes, and applies routing and cache policy. PHP generates a Content Security Policy and contains reusable helpers for session, authentication, and CSRF behavior. The current configuration abstraction combines environment overrides with an untracked server-side configuration file; it is not yet a fully managed-secret system.
Each layer reduces a different risk; no one layer proves the application is secure.
Web-server headers and transport
The current .htaccess sets controls including HSTS, X-Content-Type-Options: nosniff, and X-Frame-Options: DENY. These are useful protections, but they must be evaluated with the actual production proxy/origin configuration. HSTS, for example, only helps after HTTPS is correctly deployed and the policy is delivered over a secure connection.
Cache controls are also security controls. The Apache configuration intends to mark private pages and private APIs no-store/no-cache while assigning separate policies to public reading routes. Because later header-unset directives can affect the resulting header set, representative deployed responses must be inspected before treating that separation as verified runtime behavior.
CSP: useful, but not strict
The application constructs an allowlist-based Content Security Policy in PHP. It restricts resource categories and identifies permitted external origins, which provides more control than having no CSP. However, script-src 'unsafe-inline' substantially weakens CSP's defense against injected script. unsafe-eval permits string-to-code APIs and can expand risk where those sinks are reachable. Inline styles separately weaken protection against injected styles. Together, these exceptions mean the policy is not strict.
It would be inaccurate to call this a strict CSP. MDN's CSP guide explains why the unsafe directives weaken the policy and documents stricter browser-enforced patterns.
Sessions and CSRF
The shared security helpers configure HTTP-only cookies, enable the secure flag when HTTPS is detected, and use SameSite behavior. Authentication flows regenerate sessions. CSRF helpers generate cryptographically random, form-scoped tokens and compare them with hash_equals; multiple account and e-learning write endpoints call those helpers.
Coverage should not be overstated. A documented contact-email path currently omits CSRF validation, and coverage across legacy flows is not uniform. The OWASP CSRF Prevention Cheat Sheet describes the trade-offs between synchronizer tokens, SameSite cookies, and other controls.
Database access: a mixed migration
Newer API and e-learning code contains PDO prepared statements with bound values. Older libraries still contain mysqli_query calls and SQL assembled through interpolation. Prepared statements are therefore an implemented direction, not a universal invariant.
Escaping alone is not an equivalent architectural guarantee. The OWASP SQL Injection Prevention Cheat Sheet recommends prepared statements as the primary defense.
Secret and operational boundaries
Local Docker configuration expresses service variables, while the application configuration abstraction supports environment overrides and an untracked server-side configuration file. That file remains a legacy secret-storage boundary rather than a managed-secret service. The assistant integration uses a separate Worker and server-side proxy boundary rather than placing privileged credentials in browser code. This article intentionally omits secret names and values, operational allowlists, internal identifiers, and debug details.