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: <script>alert("xss")</script>
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>tagsjavascript: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:
- Truncates to max length
- Removes HTML tags
- 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:orfile: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:
- Removes null bytes
- Limits to max length
- Trims whitespace
- 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
- Always escape output: Use
SecurityFilter::escapeHtml()for any user-generated content - Validate input: Use
InputValidatorfor all user input - Protect forms: Use
CSRFProtectionfor all state-changing operations - Validate file paths: Use
PathValidatorbefore accessing files - Never trust user input: Always validate and sanitize
- Use parameterized queries: Prepared statements for database queries
- 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/