Add comprehensive unit tests for Security, UserModel, and Validation utilities
- Implemented SecurityTest to validate token generation, CSRF protection, input sanitization, and rate limiting. - Created UserModelTest to ensure correct database operations for user management, including creation, updating, banning, and fetching active users. - Developed ValidationTest to verify input validation and sanitization for user IDs, nicknames, messages, and API requests. - Introduced Security and Validation utility classes with methods for secure token generation, input sanitization, and comprehensive validation rules.
This commit is contained in:
parent
5692874b10
commit
41cd7a4fd8
32 changed files with 5796 additions and 368 deletions
148
bootstrap.php
Normal file
148
bootstrap.php
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
/**
|
||||
* Application Bootstrap
|
||||
* Initializes core components and security settings
|
||||
*/
|
||||
|
||||
// Start sessions securely
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
// Secure session configuration
|
||||
ini_set('session.use_strict_mode', 1);
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
ini_set('session.cookie_secure', Config::get('session.secure', false));
|
||||
ini_set('session.cookie_samesite', Config::get('session.samesite', 'Strict'));
|
||||
ini_set('session.gc_maxlifetime', Config::get('session.lifetime', 7200));
|
||||
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Load autoloader first for PSR-4 class loading
|
||||
require_once __DIR__ . '/includes/autoloader.php';
|
||||
|
||||
// Initialize configuration
|
||||
try {
|
||||
Config::load();
|
||||
} catch (Exception $e) {
|
||||
error_log("Configuration load failed: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
die("Configuration error. Please check server logs.");
|
||||
}
|
||||
|
||||
// Initialize error handling with proper environment detection
|
||||
ErrorHandler::initialize();
|
||||
|
||||
// Set PHP configuration based on environment
|
||||
if (Config::isEnvironment('production')) {
|
||||
ini_set('display_errors', 0);
|
||||
ini_set('log_errors', 1);
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
|
||||
error_log("Application running in PRODUCTION mode");
|
||||
} elseif (Config::isEnvironment('staging')) {
|
||||
ini_set('display_errors', 0);
|
||||
ini_set('log_errors', 1);
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
|
||||
error_log("Application running in STAGING mode");
|
||||
} else { // development
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('log_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
error_log("Application running in DEVELOPMENT mode");
|
||||
}
|
||||
|
||||
// Basic security headers
|
||||
function setSecurityHeaders() {
|
||||
// Prevent clickjacking
|
||||
header('X-Frame-Options: DENY');
|
||||
|
||||
// Prevent MIME sniffing
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
// Referrer policy
|
||||
header('Referrer-Policy: strict-origin-when-cross-origin');
|
||||
|
||||
// CSP headers (basic)
|
||||
$csp = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self'";
|
||||
header("Content-Security-Policy: $csp");
|
||||
|
||||
// HSTS (only in production with HTTPS)
|
||||
if (Config::isEnvironment('production') && (!empty($_SERVER['HTTPS']) || $_SERVER['SERVER_PORT'] == 443)) {
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
}
|
||||
}
|
||||
|
||||
// Input sanitization for all GET/POST data
|
||||
function sanitizeGlobalInputs() {
|
||||
$_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING) ?? [];
|
||||
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING) ?? [];
|
||||
}
|
||||
|
||||
// Rate limiting check
|
||||
function checkGlobalRateLimit() {
|
||||
$clientIP = Security::getClientIP();
|
||||
$isLimited = !Security::checkRateLimit($clientIP, 'global');
|
||||
|
||||
if ($isLimited) {
|
||||
Security::logSecurityEvent('rate_limit_exceeded', ['ip' => $clientIP]);
|
||||
http_response_code(429);
|
||||
die(json_encode(['error' => 'Too many requests. Please try again later.']));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize CSRF token if not exists
|
||||
if (!isset($_SESSION['csrf_token'])) {
|
||||
Security::generateCSRFToken();
|
||||
}
|
||||
|
||||
// Generate or validate user ID
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
$_SESSION['user_id'] = Security::generateSecureUserId();
|
||||
} elseif (!Validation::validateUserId($_SESSION['user_id'])['valid']) {
|
||||
// Regenerate invalid user ID
|
||||
$_SESSION['user_id'] = Security::generateSecureUserId();
|
||||
}
|
||||
|
||||
// Check for admin authentication state
|
||||
$isAdmin = Security::isAdminAuthenticated();
|
||||
|
||||
// Handle admin logout
|
||||
if (isset($_GET['logout']) && $isAdmin) {
|
||||
Security::logoutAdmin();
|
||||
Security::logSecurityEvent('admin_logout');
|
||||
header('Location: ' . $_SERVER['PHP_SELF']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Security checks for sensitive operations
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Validate CSRF token for POST requests
|
||||
if (!Security::validateCSRFToken()) {
|
||||
Security::logSecurityEvent('csrf_token_invalid');
|
||||
http_response_code(403);
|
||||
die(json_encode(['error' => 'Invalid security token']));
|
||||
}
|
||||
|
||||
// Detect suspicious activity
|
||||
$warnings = Security::detectSuspiciousActivity();
|
||||
if (!empty($warnings)) {
|
||||
foreach ($warnings as $warning) {
|
||||
Security::logSecurityEvent('suspicious_activity_detected', ['warning' => $warning]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply rate limiting to API endpoints
|
||||
if (strpos($_SERVER['REQUEST_URI'], '?api=') !== false ||
|
||||
strpos($_SERVER['REQUEST_URI'], '?proxy=') !== false) {
|
||||
checkGlobalRateLimit();
|
||||
}
|
||||
|
||||
// Set security headers for all responses
|
||||
setSecurityHeaders();
|
||||
|
||||
// Sanitize input data
|
||||
sanitizeGlobalInputs();
|
||||
|
||||
// Log successful bootstrap
|
||||
if (Config::isDebug()) {
|
||||
error_log("Bootstrap completed for user: " . $_SESSION['user_id'] . ($isAdmin ? ' (admin)' : ''));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue