# Security Rules

These rules are critical. Violations create exploitable vulnerabilities. Follow them without exception.

## SQL injection prevention

Never build SQL strings by concatenating or interpolating user input.

```php
// CRITICAL VIOLATION — never do this
$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
$sql = "SELECT * FROM orders WHERE email = '$email'";

// CORRECT — always use prepared statements
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$_GET['id']]);

// CORRECT — framework parameterized query
_Db::select('orders', ['email' => $email]);
```

This applies to every SQL operation: SELECT, INSERT, UPDATE, DELETE, and DDL. No exceptions for "internal" data or "trusted" sources — use parameterized queries everywhere.

Integer casting (`(int) $_GET['id']`) reduces risk but is not a substitute for prepared statements. Use both.

## XSS prevention

Never output user-supplied data to HTML without escaping.

```php
// CRITICAL VIOLATION
echo $_GET['name'];
echo $user->bio; // if bio came from user input

// CORRECT
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
echo htmlspecialchars($user->bio, ENT_QUOTES, 'UTF-8');
```

`htmlspecialchars()` with `ENT_QUOTES` and `'UTF-8'` is the minimum. Use the framework's output escaping helper if one exists.

JSON output is safe from XSS but must use `json_encode()` — never manually build JSON strings.

JavaScript context requires additional escaping beyond `htmlspecialchars()`. Use `json_encode()` to pass server data to JS.

## Password handling

Never store passwords in plaintext.

Never use MD5, SHA1, SHA256, or any fast hash for passwords. These are broken for password storage.

Always use `password_hash($password, PASSWORD_BCRYPT)` or `PASSWORD_ARGON2ID` to hash passwords.

Always use `password_verify($input, $hash)` to check passwords. Never compare hashes with `==` or `===`.

## Sensitive data in logs

Never log passwords, session tokens, credit card numbers, bank account numbers, social security numbers, or any PII.

```php
// VIOLATION
error_log('Login attempt: user=' . $email . ' password=' . $password);

// CORRECT
error_log('Login attempt: user=' . $email);
```

Before adding any log statement, review what is being logged. When in doubt, log identifiers (user ID, order ID) not values.

## Input validation

Never trust `$_GET`, `$_POST`, `$_COOKIE`, or `$_REQUEST` directly. Always validate at the boundary.

Validation means:
1. **Type check**: Is this an integer, string, email, etc.?
2. **Range/length check**: Is an integer within allowed range? Is a string within max length?
3. **Format check**: Does a date match the expected format? Does an email pass `filter_var()`?
4. **Allowlist check**: For values that must be one of a set, check against the set.

```php
// VIOLATION — no validation
$page = $_GET['page'];

// CORRECT
$page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, [
    'options' => ['min_range' => 1, 'max_range' => 1000],
    'flags' => FILTER_NULL_ON_FAILURE,
]) ?? 1;
```

## File operations

Never use user input directly in file paths.

```php
// CRITICAL VIOLATION
include($_GET['page'] . '.php');
file_get_contents('/var/uploads/' . $_GET['filename']);

// CORRECT — allowlist approach
$allowed = ['home', 'about', 'contact'];
$page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
include($page . '.php');
```

## Session security

Never put sensitive data in URLs (query strings are logged by web servers).

Regenerate session IDs after login: `session_regenerate_id(true)`.

Session cookies must be `HttpOnly` and `Secure` in production.

## Cryptography

Never implement your own encryption. Use PHP's `sodium_*` functions or a vetted library.

Never use `rand()` or `mt_rand()` for security-sensitive random values. Use `random_bytes()` or `random_int()`.
