Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- // Prevent direct access
- if (basename($_SERVER['PHP_SELF']) == basename(__FILE__)) {
- http_response_code(403);
- exit("Forbidden");
- }
- // Define application constants
- define('APP_ROOT', realpath(dirname(__FILE__)));
- define('LOG_DIR', APP_ROOT . '/logs');
- // Create log directory if it doesn't exist
- if (!is_dir(LOG_DIR)) {
- mkdir(LOG_DIR, 0750, true);
- }
- // Security Headers
- header('X-Frame-Options: SAMEORIGIN');
- header('X-XSS-Protection: 1; mode=block');
- header('X-Content-Type-Options: nosniff');
- header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none';");
- header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
- header('Referrer-Policy: same-origin');
- header('Permissions-Policy: geolocation=(), microphone=(), camera=()');
- header_remove("X-Powered-By");
- header_remove("Server");
- // Error handling
- error_reporting(0); // Set to E_ALL for development
- ini_set('display_errors', 0);
- ini_set('log_errors', 1);
- ini_set('error_log', LOG_DIR . '/security.log');
- // Check if HTTPS is in use
- if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
- // For production - redirect to HTTPS (uncomment in production)
- // header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
- // exit();
- // Log that the site was accessed without HTTPS
- error_log('Site accessed without HTTPS: ' . $_SERVER['REQUEST_URI']);
- }
- // Set PHP security configuration
- ini_set('allow_url_include', 0);
- ini_set('allow_url_fopen', 0);
- ini_set('expose_php', 0);
- ini_set('max_execution_time', 30);
- ini_set('memory_limit', '128M');
- ini_set('file_uploads', 1);
- ini_set('upload_max_filesize', '10M');
- ini_set('max_file_uploads', 5);
- ini_set('post_max_size', '20M');
- // Secure sessions
- ini_set('session.cookie_httponly', 1);
- ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on');
- ini_set('session.use_only_cookies', 1);
- ini_set('session.cookie_samesite', 'Lax');
- ini_set('session.gc_maxlifetime', 3600); // Session expires after 1 hour
- ini_set('session.use_strict_mode', 1);
- ini_set('session.sid_length', 48); // Longer session IDs
- ini_set('session.sid_bits_per_character', 6); // More entropy per character
- // Set session name with non-default value
- session_name('APP_SESSION');
- // Start the session
- session_start();
- // Session fixation & binding - enhanced version
- $sessionTimeout = 3600; // 1 hour
- if (!isset($_SESSION['initiated'])) {
- session_regenerate_id(true);
- $_SESSION['initiated'] = true;
- $_SESSION['created'] = time();
- $_SESSION['last_activity'] = time();
- $_SESSION['user_agent'] = hash('sha256', $_SERVER['HTTP_USER_AGENT']);
- $_SESSION['ip'] = hash('sha256', $_SERVER['REMOTE_ADDR']);
- } else {
- // Check for session timeout
- if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > $sessionTimeout)) {
- // Session timed out
- session_unset();
- session_destroy();
- session_start();
- session_regenerate_id(true);
- error_log("Session timeout after {$sessionTimeout} seconds of inactivity.");
- $_SESSION['auth_error'] = "Your session has expired. Please login again.";
- // Redirect to login page if needed
- // header('Location: /login.php');
- // exit();
- }
- // Check for session hijacking
- if ($_SESSION['user_agent'] !== hash('sha256', $_SERVER['HTTP_USER_AGENT']) ||
- $_SESSION['ip'] !== hash('sha256', $_SERVER['REMOTE_ADDR'])) {
- session_unset();
- session_destroy();
- session_start();
- session_regenerate_id(true);
- error_log("Session hijack attempt blocked. IP: {$_SERVER['REMOTE_ADDR']}, User-Agent: {$_SERVER['HTTP_USER_AGENT']}");
- $_SESSION['auth_error'] = "Session authentication error. Please login again.";
- // Redirect to login page if needed
- // header('Location: /login.php');
- exit("Session Error");
- }
- // Regenerate session ID periodically (every 30 minutes)
- if (isset($_SESSION['created']) && (time() - $_SESSION['created'] > 1800)) {
- session_regenerate_id(true);
- $_SESSION['created'] = time();
- }
- // Update last activity time
- $_SESSION['last_activity'] = time();
- }
- // Rate limiting for certain actions (example for login attempts)
- function rateLimit($action, $limit = 5, $timeframe = 300) {
- // Simple rate limiting based on IP and action
- $ip = $_SERVER['REMOTE_ADDR'];
- $timestamp = time();
- $logFile = LOG_DIR . '/rate_limits.json';
- // Create or load rate limit data
- if (file_exists($logFile)) {
- $data = json_decode(file_get_contents($logFile), true);
- } else {
- $data = [];
- }
- // Clean up old entries
- foreach ($data as $key => $entries) {
- foreach ($entries as $i => $entry) {
- if ($timestamp - $entry > $timeframe) {
- unset($data[$key][$i]);
- }
- }
- if (empty($data[$key])) {
- unset($data[$key]);
- }
- }
- // Check rate limit
- $key = $ip . '_' . $action;
- if (!isset($data[$key])) {
- $data[$key] = [];
- }
- if (count($data[$key]) >= $limit) {
- // Rate limit exceeded
- error_log("Rate limit exceeded for {$action} from IP {$ip}");
- http_response_code(429);
- exit('Too many requests. Please try again later.');
- }
- // Add current timestamp to the log
- $data[$key][] = $timestamp;
- // Save updated data
- file_put_contents($logFile, json_encode($data), LOCK_EX);
- return true;
- }
- // Input sanitization - Enhanced version
- function clean_input($data) {
- if (is_array($data)) {
- $cleaned = [];
- foreach ($data as $key => $value) {
- $cleaned[$key] = clean_input($value);
- }
- return $cleaned;
- }
- return htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8');
- }
- // Input validation functions
- function validate_email($email) {
- return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
- }
- function validate_int($value, $min = null, $max = null) {
- $options = [];
- if ($min !== null) $options['min_range'] = $min;
- if ($max !== null) $options['max_range'] = $max;
- return filter_var($value, FILTER_VALIDATE_INT, ['options' => $options]) !== false;
- }
- function validate_url($url) {
- return filter_var($url, FILTER_VALIDATE_URL) !== false;
- }
- // Enhanced recursive sanitization of input arrays
- function sanitize_inputs(&$array) {
- foreach ($array as $key => &$value) {
- if (is_array($value)) {
- sanitize_inputs($value);
- } else {
- $value = clean_input($value);
- }
- }
- }
- // Apply sanitization to all input sources
- sanitize_inputs($_GET);
- sanitize_inputs($_POST);
- sanitize_inputs($_COOKIE);
- // Don't sanitize $_FILES
- // Safe include with better path handling
- function safe_include($file) {
- // Convert to absolute path if relative path provided
- if (!preg_match('/^\//', $file)) {
- $file = APP_ROOT . '/' . $file;
- }
- $realpath = realpath($file);
- if ($realpath && strpos($realpath, APP_ROOT) === 0 && file_exists($realpath)) {
- include $realpath;
- return true;
- } else {
- error_log("Blocked unsafe include attempt: $file");
- http_response_code(403);
- exit("Invalid include");
- }
- }
- // CSRF protection - Enhanced version
- function generate_csrf_token() {
- if (empty($_SESSION['csrf_token']) || empty($_SESSION['csrf_token_time']) ||
- (time() - $_SESSION['csrf_token_time']) > 3600) {
- $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
- $_SESSION['csrf_token_time'] = time();
- }
- return $_SESSION['csrf_token'];
- }
- function csrf_token_tag() {
- $token = generate_csrf_token();
- return '<input type="hidden" name="csrf_token" value="' . $token . '">';
- }
- function verify_csrf_token($token = null) {
- if ($token === null) {
- // Try to get token from POST or headers
- if (isset($_POST['csrf_token'])) {
- $token = $_POST['csrf_token'];
- } elseif (isset($_SERVER['HTTP_X_CSRF_TOKEN'])) {
- $token = $_SERVER['HTTP_X_CSRF_TOKEN'];
- } else {
- return false;
- }
- }
- if (!isset($_SESSION['csrf_token']) || empty($token)) {
- return false;
- }
- return hash_equals($_SESSION['csrf_token'], $token);
- }
- // SQL injection prevention helper (when using mysqli)
- function escape_sql($conn, $input) {
- if (is_array($input)) {
- $escaped = [];
- foreach ($input as $key => $value) {
- $escaped[$key] = escape_sql($conn, $value);
- }
- return $escaped;
- }
- return mysqli_real_escape_string($conn, $input);
- }
- // Prepared statement wrapper for mysqli
- function execute_query($conn, $sql, $params = [], $types = '') {
- if (empty($params)) {
- return $conn->query($sql);
- }
- $stmt = $conn->prepare($sql);
- if (!$stmt) {
- error_log("SQL Prepare Error: " . $conn->error . " in query: " . $sql);
- return false;
- }
- // If types string not provided, generate it
- if (empty($types)) {
- foreach ($params as $param) {
- if (is_int($param)) {
- $types .= 'i';
- } elseif (is_float($param)) {
- $types .= 'd';
- } elseif (is_string($param)) {
- $types .= 's';
- } else {
- $types .= 'b';
- }
- }
- }
- if (!empty($params)) {
- $stmt->bind_param($types, ...$params);
- }
- $stmt->execute();
- return $stmt;
- }
- // File upload security
- function secure_file_upload($file, $allowedTypes, $maxSize, $uploadDir) {
- // Check if upload directory exists and is writable
- if (!is_dir($uploadDir)) {
- mkdir($uploadDir, 0750, true);
- }
- if (!is_writable($uploadDir)) {
- error_log("Upload directory not writable: $uploadDir");
- return ['error' => 'Server configuration error.'];
- }
- // Basic error checking
- if (!isset($file['error']) || is_array($file['error'])) {
- return ['error' => 'Invalid file parameters.'];
- }
- // Check file upload errors
- switch ($file['error']) {
- case UPLOAD_ERR_OK:
- break;
- case UPLOAD_ERR_INI_SIZE:
- case UPLOAD_ERR_FORM_SIZE:
- return ['error' => 'File too large.'];
- case UPLOAD_ERR_PARTIAL:
- return ['error' => 'File upload was not completed.'];
- case UPLOAD_ERR_NO_FILE:
- return ['error' => 'No file was uploaded.'];
- case UPLOAD_ERR_NO_TMP_DIR:
- case UPLOAD_ERR_CANT_WRITE:
- case UPLOAD_ERR_EXTENSION:
- return ['error' => 'Server file upload error.'];
- default:
- return ['error' => 'Unknown upload error.'];
- }
- // Check filesize
- if ($file['size'] > $maxSize) {
- return ['error' => 'File too large.'];
- }
- // Check MIME type
- $finfo = new finfo(FILEINFO_MIME_TYPE);
- $fileType = $finfo->file($file['tmp_name']);
- if (!in_array($fileType, $allowedTypes)) {
- return ['error' => 'File type not allowed.'];
- }
- // Create safe filename
- $filename = basename($file['name']);
- $filename = preg_replace('/[^a-zA-Z0-9_.-]/', '_', $filename);
- $filename = time() . '_' . $filename;
- $uploadFile = $uploadDir . '/' . $filename;
- // Check for PHP code in files that shouldn't have it
- if (!in_array($fileType, ['application/x-php', 'text/x-php'])) {
- $fileContent = file_get_contents($file['tmp_name']);
- if (preg_match('/<\?php/i', $fileContent)) {
- return ['error' => 'File contains malicious code.'];
- }
- }
- // Move the file
- if (move_uploaded_file($file['tmp_name'], $uploadFile)) {
- // Set proper permissions
- chmod($uploadFile, 0640);
- return [
- 'success' => true,
- 'filename' => $filename,
- 'path' => $uploadFile
- ];
- }
- return ['error' => 'Failed to save file.'];
- }
- // Password hashing function
- function hash_password($password) {
- return password_hash($password, PASSWORD_ARGON2ID, [
- 'memory_cost' => 65536, // 64MB
- 'time_cost' => 4, // 4 iterations
- 'threads' => 2 // 2 threads
- ]);
- }
- // Password verification
- function verify_password($password, $hash) {
- return password_verify($password, $hash);
- }
- // Request method validation
- function require_method($method) {
- if ($_SERVER['REQUEST_METHOD'] !== strtoupper($method)) {
- http_response_code(405);
- header('Allow: ' . strtoupper($method));
- exit('Method Not Allowed');
- }
- return true;
- }
- // Logger function
- function security_log($message, $level = 'INFO') {
- $timestamp = date('Y-m-d H:i:s');
- $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'Unknown';
- $user = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 'Guest';
- $logMessage = "[$timestamp][$level][$ip][$user] $message" . PHP_EOL;
- error_log($logMessage, 3, LOG_DIR . '/security.log');
- }
- // Environment check for additional security in production
- function is_production() {
- // This is a simple example - define a more robust method for your environment
- return (!isset($_SERVER['SERVER_NAME']) ||
- !preg_match('/(localhost|127.0.0.1|.local|.test)$/', $_SERVER['SERVER_NAME']));
- }
- // If in production, apply stricter security measures
- if (is_production()) {
- // Additional production-only security measures
- ini_set('session.cookie_secure', 1); // Force secure cookies
- ini_set('session.gc_maxlifetime', 1800); // 30 minutes session timeout
- }
- // Simple firewall - block suspicious requests
- $blocked_keywords = [
- 'eval(', 'UNION+SELECT', 'UNION SELECT', 'concat(', 'group_concat(',
- 'LOAD_FILE', 'outfile', 'dumpfile', '<script', 'javascript:',
- 'onload=', 'onerror=', 'onclick=', 'onmouseover='
- ];
- foreach ($_GET as $key => $value) {
- foreach ($blocked_keywords as $keyword) {
- if (stripos($value, $keyword) !== false || stripos($key, $keyword) !== false) {
- security_log("Blocked request with suspicious GET parameter: $key=$value", 'WARNING');
- http_response_code(403);
- exit('Access Denied');
- }
- }
- }
- foreach ($_POST as $key => $value) {
- if (is_string($value)) {
- foreach ($blocked_keywords as $keyword) {
- if (stripos($value, $keyword) !== false || stripos($key, $keyword) !== false) {
- security_log("Blocked request with suspicious POST parameter: $key=$value", 'WARNING');
- http_response_code(403);
- exit('Access Denied');
- }
- }
- }
- }
- // Check for common attack vectors in URL
- $uri = $_SERVER['REQUEST_URI'];
- foreach ($blocked_keywords as $keyword) {
- if (stripos($uri, $keyword) !== false) {
- security_log("Blocked request with suspicious URI: $uri", 'WARNING');
- http_response_code(403);
- exit('Access Denied');
- }
- }
- // Anti-automation - Block requests with no user agent
- if (empty($_SERVER['HTTP_USER_AGENT'])) {
- security_log("Blocked request with no user agent", 'WARNING');
- http_response_code(403);
- exit('Access Denied');
- }
- // Register shutdown function for additional logging
- register_shutdown_function(function() {
- $error = error_get_last();
- if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
- security_log("Fatal error: {$error['message']} in {$error['file']} on line {$error['line']}", 'ERROR');
- }
- });
- ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement