menu_book Navigation menu

Security Foundations

Security is enforced as a core architectural principle, not an optional layer. It is not an afterthought or an external plugin; it is woven into the core rendering and routing pipeline. As a developer, you are expected to follow these foundations to maintain site integrity.

1. SQL Injection Prevention (Database Layer)

Pragma CMS strictly forbids raw SQL queries containing variables. We use Prepared Statements for every database interaction.

How it works

The Database class acts as a secure wrapper around PDO. Instead of injecting variables directly into the SQL string, you use placeholders. The database engine then compiles the SQL logic and the data separately, making it impossible for a malicious string to alter the query's structure.

  • Rule: Never use $variable inside an SQL string.
  • Method: Use Database::fetchAll(), Database::fetch(), Database::fetchColumn() or Database::updateTable() with a parameters array.
PHP
// ❌ DANGEROUS: Vulnerable to SQL Injection
$id = $_GET['id'];
Database::fetchAll("SELECT * FROM entries WHERE entry_id = $id");

// ✅ SECURE: Uses Prepared Statements
Database::fetchAll("SELECT * FROM entries WHERE entry_id = :id", ['id' => $id]);

2. XSS Protection (Cross-Site Scripting)

Pragma CMS employs a dual-layer strategy to prevent malicious scripts from being executed in the user's browser.

A. Output Escaping (GET phase)

For 90% of your data (titles, names, meta descriptions), use htmlspecialchars() during the rendering phase. This is ultra-fast and converts dangerous characters into harmless HTML entities.

PHP
// ✅ Always escape standard fields on output
<h1><?= htmlspecialchars($page->entry['title']); ?></h1>

B. Input Sanitization (POST phase)

When a field must allow rich HTML (e.g., from a WYSIWYG editor), we use sanitizeHTML(). Because this process is resource-intensive (parsing the full DOM tree), it is strictly performed before database insertion during a POST request. This ensures that the data stored in your database is already "clean" and safe to echo directly later.

PHP
// ✅ Sanitize rich content ONCE during POST
$cleanContent = sanitizeHTML($_POST['content'], 'default');
Database::updateTable("UPDATE entries SET content = :html ...", ['html' => $cleanContent]);

3. CSRF Protection (Cross-Site Request Forgery)

Every user session in Pragma CMS is assigned a unique, cryptographically strong csrf_token.

The Mechanism

  1. Generation: The router.php generates the token at the start of the session.
  2. Injection: Every form includes a hidden input containing this token.
  3. Validation: On every POST request, the router or the controller compares the submitted token with the one stored in the session using hash_equals(). If they don't match, the request is instantly rejected (403 Access Denied).
PHP
// Form implementation
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">

4. Content Security Policy (CSP) & Nonces

The router.php dynamically generates a cryptographic Nonce (number used once) for every page load. This is our primary defense against unauthorized inline scripts.

Even if an attacker manages to inject a <script> tag into your page, the browser will refuse to execute it unless it possesses the correct secret nonce for that specific request.

PHP
// Authorizing an inline script
<script nonce="<?= $GLOBALS['csp_nonce'] ?>">
    // My secure script
</script>

5. Transport Security (Man-in-the-Middle)

Pragma CMS is designed for the modern web. The router automatically detects if the site is running on HTTPS and injects:

  • HSTS Headers: Instructing the browser to only interact with the site over secure connections.
  • Upgrade-Insecure-Requests: Automatically converting any accidental HTTP asset calls into HTTPS.

The Developer’s Golden Rules

  1. Assume all Input is Evil: Never trust $_GET, $_POST, or $_COOKIE. Always validate types and formats.
  2. Escape on Output: Use htmlspecialchars() by default unless the field is explicitly marked as rich HTML.
  3. Use the Database Wrapper: Never use native PHP mysql_ or pdo functions directly; use the Database class to ensure logging and prepared statements are active.
  4. No Nonce, No Script: Never add inline <script> tags without the CMS nonce attribute.