Security Module API Documentation

Security Module API Documentation

Overview

The Blog\Security namespace provides comprehensive security features for the blog system, including XSS protection, CSRF protection, input validation, and path validation to prevent common web vulnerabilities.

Namespace

namespace Blog\Security;

Classes

SecurityFilter

Static utility class for filtering and escaping output to prevent XSS attacks.

Public Methods

escapeHtml(string $input, string $encoding = 'UTF-8'): string

Escapes HTML special characters to prevent XSS.

Parameters:

  • $input (string): Input string to escape
  • $encoding (string): Character encoding (default: UTF-8)

Returns:

  • (string): Escaped HTML-safe string

Example:

echo SecurityFilter::escapeHtml('<script>alert("xss")</script>');
// Output: &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;
escapeAttribute(string $input, string $encoding = 'UTF-8'): string

Escapes HTML attribute values.

Parameters:

  • $input (string): Attribute value to escape
  • $encoding (string): Character encoding (default: UTF-8)

Returns:

  • (string): Escaped attribute-safe string
escapeUrl(string $url): string

Filters URLs to prevent URL injection attacks.

Parameters:

  • $url (string): URL to filter

Returns:

  • (string): Safe URL or empty string if malicious

Features:

  • Validates absolute URLs (http://, https://)
  • Blocks malicious protocols (javascript:, vbscript:, data:, file:, about:)
  • Escapes HTML characters
  • Allows relative paths

Example:

SecurityFilter::escapeUrl('https://example.com'); // https://example.com
SecurityFilter::escapeUrl('javascript:alert(1)'); // (empty)
escapeJs(string $input, string $encoding = 'UTF-8'): string

Escapes strings for use in JavaScript.

Parameters:

  • $input (string): Input string to escape
  • $encoding (string): Character encoding (default: UTF-8)

Returns:

  • (string): JavaScript-safe string
sanitizeMarkdown(string $markdown): string

Removes dangerous HTML patterns from Markdown content.

Parameters:

  • $markdown (string): Markdown content to sanitize

Returns:

  • (string): Sanitized Markdown content

Removed Patterns:

  • <script> tags
  • <iframe> tags
  • <object> tags
  • <embed> tags
  • javascript: protocol
  • Event handlers (onclick, onerror, etc.)
  • data: URIs with base64
sanitizeUserInput(string $input, int $maxLength = 10000): string

Sanitizes user input by removing HTML tags and escaping.

Parameters:

  • $input (string): User input to sanitize
  • $maxLength (int): Maximum allowed length (default: 10000)

Returns:

  • (string): Sanitized input

Process:

  1. Truncates to max length
  2. Removes HTML tags
  3. Escapes special characters
isSafePath(string $path): bool

Checks if a file path is safe from directory traversal attacks.

Parameters:

  • $path (string): File path to validate

Returns:

  • (bool): True if safe, false otherwise

Checks:

  • Path must exist (realpath)
  • No .. sequences
  • No protocol separators (://)
  • No data: or file: protocols

CSRFProtection

Protects against Cross-Site Request Forgery (CSRF) attacks using session-based tokens.

Constructor

public function __construct(string $tokenName = 'csrf_token', int $tokenExpiration = 3600)

Parameters:

  • $tokenName (string): Name of the CSRF token field (default: 'csrf_token')
  • $tokenExpiration (int): Token expiration time in seconds (default: 3600 = 1 hour)

Public Methods

generateToken(): string

Generates a new CSRF token and stores it in the session.

Returns:

  • (string): 64-character hex token
validateToken(string $token): bool

Validates a CSRF token.

Parameters:

  • $token (string): Token to validate

Returns:

  • (bool): True if valid, false otherwise

Validation:

  • Token must exist in session
  • Token must not be expired
  • Uses constant-time comparison (hash_equals)
validateRequest(): bool

Validates CSRF token from current request (POST or GET).

Returns:

  • (bool): True if valid, false otherwise

Note: Token remains valid after validation (reusable).

validateRequestOnce(): bool

Validates and immediately revokes CSRF token (one-time use).

Returns:

  • (bool): True if valid, false otherwise

Use Case: Prevents form resubmission.

getHiddenField(): string

Generates HTML hidden input field for CSRF protection.

Returns:

  • (string): <input type="hidden"> HTML element

Example:

$csrf = new CSRFProtection();
echo $csrf->getHiddenField();
// Output: <input type="hidden" name="csrf_token" value="abc123...">
getToken(): string

Gets a CSRF token (for AJAX requests).

Returns:

  • (string): New CSRF token
revokeToken(string $token): bool

Revokes (deletes) a specific CSRF token.

Parameters:

  • $token (string): Token to revoke

Returns:

  • (bool): True if token was found and revoked
clearAllTokens(): void

Removes all CSRF tokens from the session.

getAllTokens(): array

Returns all valid, non-expired tokens.

Returns:

  • (array): Array of token strings
getTokenMetadata(): array

Returns metadata for all valid tokens.

Returns:

  • (array): Array of metadata with keys:

- createdat</code>: Token creation timestamp - <code>expiresat: Token expiration timestamp - remaining_time: Seconds until expiration

Static Methods

protectForm(): string

Convenience method to protect a form (generates hidden field).

Returns:

  • (string): CSRF hidden field HTML

Example:

<form method="POST">
    <?php echo CSRFProtection::protectForm(); ?>
    <input type="text" name="username">
    <button type="submit">Submit</button>
</form>
validateForm(): bool

Convenience method to validate form submission.

Returns:

  • (bool): True if CSRF token is valid

Example:

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!CSRFProtection::validateForm()) {
        die('CSRF token validation failed');
    }
    // Process form...
}

InputValidator

Validates and sanitizes user input with comprehensive type checking.

Public Static Methods

validateLength(string $input, int $min = 0, int $max = 10000): bool

Validates string length (multi-byte aware).

Parameters:

  • $input (string): Input string
  • $min (int): Minimum length (default: 0)
  • $max (int): Maximum length (default: 10000)

Returns:

  • (bool): True if length is within range
validateAlnum(string $input, bool $allowSpaces = false): bool

Validates that input contains only alphanumeric characters.

Parameters:

  • $input (string): Input string
  • $allowSpaces (bool): Allow spaces (default: false)

Returns:

  • (bool): True if valid
validateAlpha(string $input, bool $allowSpaces = false): bool

Validates that input contains only letters.

Parameters:

  • $input (string): Input string
  • $allowSpaces (bool): Allow spaces (default: false)

Returns:

  • (bool): True if valid
validateInt($input, ?int $min = null, ?int $max = null): bool

Validates integer input.

Parameters:

  • $input (mixed): Input to validate
  • $min (int|null): Minimum value (optional)
  • $max (int|null): Maximum value (optional)

Returns:

  • (bool): True if valid integer within range
validateFloat($input, ?float $min = null, ?float $max = null): bool

Validates float input.

Parameters:

  • $input (mixed): Input to validate
  • $min (float|null): Minimum value (optional)
  • $max (float|null): Maximum value (optional)

Returns:

  • (bool): True if valid float within range
validateEmail(string $input): bool

Validates email address format.

Parameters:

  • $input (string): Email address

Returns:

  • (bool): True if valid email
validateUrl(string $input): bool

Validates URL format.

Parameters:

  • $input (string): URL to validate

Returns:

  • (bool): True if valid URL
validateIpv4(string $input): bool

Validates IPv4 address.

Parameters:

  • $input (string): IPv4 address

Returns:

  • (bool): True if valid IPv4
validateIpv6(string $input): bool

Validates IPv6 address.

Parameters:

  • $input (string): IPv6 address

Returns:

  • (bool): True if valid IPv6
validateBool($input): bool

Validates boolean input.

Parameters:

  • $input (mixed): Input to validate

Returns:

  • (bool): True if valid boolean

Accepted Values:

  • true, false (boolean)
  • 1, 0 (integer)
  • 'true', 'false' (string)
  • 'yes', 'no' (string)
sanitize(string $input, int $maxLength = 10000, bool $allowHtml = false): string

Sanitizes user input.

Parameters:

  • $input (string): Input to sanitize
  • $maxLength (int): Maximum length (default: 10000)
  • $allowHtml (bool): Allow HTML (default: false)

Returns:

  • (string): Sanitized input

Process:

  1. Removes null bytes
  2. Limits to max length
  3. Trims whitespace
  4. Escapes HTML (if not allowed)
validateFilename(string $filename): bool

Validates filename for security.

Parameters:

  • $filename (string): Filename to validate

Returns:

  • (bool): True if safe filename

Checks:

  • Not empty
  • No path traversal (..)
  • No path separators (/, ``)
  • No dangerous characters (<, >, :, ", |, ?, *)
  • Max 255 characters
validateArray($input): bool

Validates input is a non-empty array.

Parameters:

  • $input (mixed): Input to validate

Returns:

  • (bool): True if non-empty array
validateEnum($input, array $allowedValues): bool

Validates input matches one of allowed values.

Parameters:

  • $input (mixed): Input to validate
  • $allowedValues (array): Array of allowed values

Returns:

  • (bool): True if input is in allowed values
validateRegex(string $pattern): bool

Validates regex pattern syntax.

Parameters:

  • $pattern (string): Regex pattern

Returns:

  • (bool): True if valid pattern
validateDateTime(string $input, string $format = 'Y-m-d\TH:i:sP'): bool

Validates datetime string.

Parameters:

  • $input (string): DateTime string
  • $format (string): Expected format (default: ISO 8601)

Returns:

  • (bool): True if valid datetime
validateJson(string $input): bool

Validates JSON string.

Parameters:

  • $input (string): JSON string

Returns:

  • (bool): True if valid JSON
sanitizeArray(array $input, int $maxLength = 1000): array

Recursively sanitizes array input.

Parameters:

  • $input (array): Input array
  • $maxLength (int): Max length per element (default: 1000)

Returns:

  • (array): Sanitized array
getRequestParam(string $param, string $type = 'string', $default = null)

Type-safe parameter retrieval from GET/POST.

Parameters:

  • $param (string): Parameter name
  • $type (string): Type ('string', 'int', 'float', 'bool', 'email', 'url', 'array')
  • $default (mixed): Default value if not found or invalid

Returns:

  • (mixed): Validated value or default

Example:

$page = InputValidator::getRequestParam('page', 'int', 1);
$email = InputValidator::getRequestParam('email', 'email', null);

PathValidator

Prevents path traversal attacks by validating file paths.

Constructor

public function __construct(string $basePath = null)

Parameters:

  • $basePath (string): Base directory path (default: parent of _blog directory)

Public Methods

validate(string $path): bool

Validates that a path is within the allowed base directory.

Parameters:

  • $path (string): Path to validate

Returns:

  • (bool): True if path is safe and within base directory

Checks:

  • Path must exist (realpath)
  • Path must be within base directory
  • Normalizes path separators
getRelativePath(string $path): ?string

Gets path relative to base directory.

Parameters:

  • $path (string): Full path

Returns:

  • (string|null): Relative path or null if outside base directory
getSafePath(string $path): ?string

Validates and returns safe absolute path.

Parameters:

  • $path (string): Path to validate

Returns:

  • (string|null): Safe absolute path or null if invalid
isFile(string $path): bool

Checks if path is a valid file within base directory.

Parameters:

  • $path (string): Path to check

Returns:

  • (bool): True if path is a valid file
isDirectory(string $path): bool

Checks if path is a valid directory within base directory.

Parameters:

  • $path (string): Path to check

Returns:

  • (bool): True if path is a valid directory
validateFilename(string $filename): bool

Validates filename for security.

Parameters:

  • $filename (string): Filename to validate

Returns:

  • (bool): True if safe filename

Checks:

  • No path separators
  • No . or ..
  • No Windows forbidden characters (<, >, :, ", |, ?, *)
  • Max 255 characters
  • Not hidden (doesn't start with .)
validateExtension(string $path, array $allowedExtensions): bool

Validates file extension is in allowed list.

Parameters:

  • $path (string): File path
  • $allowedExtensions (array): Allowed extensions (e.g., ['md', 'php'])

Returns:

  • (bool): True if extension is allowed
validateMarkdownFile(string $path): bool

Validates path is a valid Markdown file.

Parameters:

  • $path (string): File path

Returns:

  • (bool): True if valid Markdown file
validatePhpFile(string $path): bool

Validates path is a valid PHP file.

Parameters:

  • $path (string): File path

Returns:

  • (bool): True if valid PHP file

Usage Examples

XSS Protection

<?php
use Blog\Security\SecurityFilter;

// Escape HTML output
$userInput = '<script>alert("xss")</script>';
echo SecurityFilter::escapeHtml($userInput);

// Escape URL
$url = $_GET['redirect'] ?? 'index.php';
echo '<a href="' . SecurityFilter::escapeUrl($url) . '">Link</a>';

CSRF Protection

<?php
use Blog\Security\CSRFProtection;

$csrf = new CSRFProtection();

// In form
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $csrfField = $csrf->getHiddenField();
    echo <<<FORM
<form method="POST">
    $csrfField
    <input type="text" name="data">
    <button type="submit">Submit</button>
</form>
FORM;
}

// On form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if ($csrf->validateRequestOnce()) {
        // Process form
    } else {
        die('Invalid CSRF token');
    }
}

Input Validation

<?php
use Blog\Security\InputValidator;

// Validate email
$email = $_POST['email'] ?? '';
if (!InputValidator::validateEmail($email)) {
    die('Invalid email address');
}

// Get validated integer
$page = InputValidator::getRequestParam('page', 'int', 1);
if ($page < 1 || $page > 100) {
    die('Invalid page number');
}

// Sanitize user input
$comment = InputValidator::sanitize($_POST['comment'], 5000);

Path Validation

<?php
use Blog\Security\PathValidator;

$validator = new PathValidator(__DIR__ . '/uploads');

// Validate file path
$userFile = $_GET['file'] ?? '';
if ($validator->validate($userFile)) {
    $safePath = $validator->getSafePath($userFile);
    echo file_get_contents($safePath);
} else {
    http_response_code(403);
    die('Access denied');
}

Security Best Practices

  1. Always escape output: Use SecurityFilter::escapeHtml() for any user-generated content
  2. Validate input: Use InputValidator for all user input
  3. Protect forms: Use CSRFProtection for all state-changing operations
  4. Validate file paths: Use PathValidator before accessing files
  5. Never trust user input: Always validate and sanitize
  6. Use parameterized queries: Prepared statements for database queries
  7. Keep dependencies updated: Regular security updates

Testing

All security classes have comprehensive test coverage:

  • SecurityFilter: 36 test cases
  • CSRFProtection: 17 test cases
  • InputValidator: 62 test cases
  • PathValidator: 41 test cases

Run tests with:

vendor/bin/phpunit tests/
← 返回目录