# Security Skill ## Purpose Use this skill for input validation, output escaping, passwords, authentication, authorization, sessions, CSRF, secrets, error disclosure, dangerous functions, serialization, and file/path safety. --- ## Security Baseline Treat all external data as untrusted. Untrusted data includes: - `$_GET` - `$_POST` - `$_REQUEST` - `$_COOKIE` - `$_SERVER` - uploaded files - request bodies - session values - database values originally supplied by users - third-party API responses --- ## Input Validation Validate on input. Example: ```php $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); if ($email === false || $email === null) { throw new InvalidArgumentException('A valid email address is required.'); } ``` Rules: - Validate type, range, length, format, and allowed values. - Validate server-side even when client-side validation exists. - Reject unexpected fields when appropriate. - Normalize data intentionally, not accidentally. --- ## Output Escaping Escape on output based on context. For HTML output: ```php function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } ``` Usage: ```php

name()) ?>

``` Rules: - Escape based on context: HTML, attribute, JavaScript, CSS, URL, SQL, shell. - Do not use the same escaping function for every context. - Prefer template engines with automatic escaping only when appropriate for the project. - Avoid allowing raw HTML from users. If required, sanitize with a proven whitelist sanitizer. - Use `escapeshellarg()` when passing controlled values to shell commands, and avoid shell execution when possible. - Never trust file paths supplied by users. - Reject path traversal values such as `../`, `/`, `\`, and null bytes when user-provided paths are not allowed. --- ## Passwords and Authentication Never store plain-text passwords. Use PHP’s password API: ```php $hash = password_hash($plainPassword, PASSWORD_DEFAULT); if (! password_verify($plainPassword, $hash)) { throw new RuntimeException('Invalid credentials.'); } ``` Rules: - Use `password_hash()` for new password hashes. - Use `password_verify()` for login checks. - Use `password_needs_rehash()` when algorithm/cost settings change. - Do not create your own password hashing algorithm. - Do not use general-purpose hashes like `md5`, `sha1`, or raw `sha256` for passwords. - Rate-limit login attempts. - Regenerate session IDs after login. - Use secure, HTTP-only, SameSite cookies for sessions. --- ## Authorization - Check authorization separately from authentication. - Do not assume logged-in means allowed. - Enforce permissions server-side. - Avoid hiding buttons as the only authorization control. - Prefer explicit permission checks near protected actions or service boundaries. --- ## CSRF Use CSRF protection for state-changing forms and unsafe HTTP methods. State-changing actions include: - Create - Update - Delete - Login/logout state changes - Password changes - Email changes - Permission changes --- ## Serialization and Data Exchange Do not call `unserialize()` on untrusted data. Prefer JSON for data exchange: ```php $data = json_decode($json, true, flags: JSON_THROW_ON_ERROR); $json = json_encode($data, JSON_THROW_ON_ERROR); ``` Rules: - Use `JSON_THROW_ON_ERROR` for new code. - Validate decoded data before using it. - Avoid PHP serialization for data that crosses trust boundaries. --- ## Configuration and Secrets Rules: - Keep secrets out of source control. - Do not commit passwords, API keys, private keys, tokens, or production DSNs. - Store configuration outside the public web root. - Use environment variables or ignored local config files for secrets. - Provide a safe example file such as `.env.example`. Example `.gitignore` entries: ```text .env .env.local /config/local.php /var/cache/ /var/log/ /vendor/ ``` --- ## Error Handling and Logging Development: - Show errors locally. - Log errors. - Use Xdebug when debugging complex issues. Production: - Do not display errors to users. - Log errors to a secure log destination. - Return safe, generic error messages. - Preserve enough context in logs for troubleshooting. Do not leak: - stack traces to users - SQL statements with secrets - environment variables - full filesystem paths - tokens or passwords Example: ```php try { $service->handle($request); } catch (Throwable $e) { $logger->error('Order processing failed.', [ 'exception' => $e, 'requestId' => $requestId, ]); http_response_code(500); echo 'An unexpected error occurred.'; } ``` --- ## Security Checklist Before completing any feature, verify: - [ ] All external input is validated. - [ ] All output is escaped for the correct context. - [ ] SQL uses prepared statements or safe query builders. - [ ] Authentication and authorization are checked server-side. - [ ] Secrets are not committed. - [ ] Errors are not exposed in production responses. - [ ] File uploads validate size, extension, MIME type, and storage path. - [ ] Passwords use `password_hash()` and `password_verify()`. - [ ] CSRF protection exists for state-changing requests. - [ ] Dangerous functions are avoided or justified: `eval`, `exec`, `shell_exec`, `system`, `passthru`, `unserialize`. - [ ] Dependencies have no known vulnerabilities according to `composer audit`.