← Back
Editing: wp-password-protect.php
<?php declare(strict_types=1); /** * Plugin Name: Simple Password Protect * Plugin URI: https://wordpress.org/plugins/simple-password-protect * Description: A simple password protection system for WordPress sites that protects all frontend pages with a single password. * Version: 1.1.0 * Author: Tobias Kurth * Author URI: https://desk9.design/ * License: GPL v2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Text Domain: simple-password-protect * Domain Path: /languages * Requires at least: 6.8 * Requires PHP: 7.4 * * @package SimplePasswordProtect */ // Prevent direct access if (!defined('ABSPATH')) { exit; } // Define plugin constants define('SPWDPROT_VERSION', '1.1.0'); define('SPWDPROT_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('SPWDPROT_PLUGIN_URL', plugin_dir_url(__FILE__)); define('SPWDPROT_PLUGIN_FILE', __FILE__); define('SPWDPROT_PLUGIN_BASENAME', plugin_basename(__FILE__)); /** * Main Plugin Class */ class Spwdprot_Main { /** * Plugin instance * * @var Spwdprot_Main|null */ private static ?Spwdprot_Main $instance = null; /** * Cookie name for authentication */ private const COOKIE_NAME = 'wp_password_protect_auth'; /** * Cookie expiration time (24 hours) */ private const COOKIE_EXPIRY = 86400; /** * Get plugin instance (Singleton pattern) * * @return Spwdprot_Main */ public static function getInstance(): Spwdprot_Main { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Constructor - Initialize plugin */ private function __construct() { // Load dependencies first $this->loadDependencies(); // Initialize hooks after dependencies are loaded $this->initHooks(); } /** * Load plugin dependencies */ private function loadDependencies(): void { try { // Load helper functions first $helpers_file = SPWDPROT_PLUGIN_DIR . 'includes/helpers.php'; if (file_exists($helpers_file)) { require_once $helpers_file; } else { $this->logError('Helper file not found: ' . $helpers_file); } // Only load admin settings if file exists $admin_file = SPWDPROT_PLUGIN_DIR . 'admin/settings.php'; if (file_exists($admin_file)) { require_once $admin_file; } else { $this->logError('Admin file not found: ' . $admin_file); } } catch (Exception $e) { $this->logError('Failed to load dependencies: ' . $e->getMessage()); } } /** * Log errors using WordPress debug logging */ private function logError(string $message): void { if (WP_DEBUG && WP_DEBUG_LOG) { // Use WordPress built-in logging instead of error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('Simple Password Protect Plugin: ' . $message); } } /** * Initialize WordPress hooks */ private function initHooks(): void { // Plugin activation/deactivation hooks register_activation_hook(SPWDPROT_PLUGIN_FILE, [$this, 'activate']); register_deactivation_hook(SPWDPROT_PLUGIN_FILE, [$this, 'deactivate']); // WordPress initialization hooks add_action('init', [$this, 'init']); add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']); add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']); // Template redirect hook for password protection (lower priority to let WP initialize) add_action('template_redirect', [$this, 'checkPasswordProtection'], 10); // AJAX handlers add_action('wp_ajax_nopriv_spwdprot_verify_password', [$this, 'ajaxVerifyPassword']); add_action('wp_ajax_spwdprot_verify_password', [$this, 'ajaxVerifyPassword']); add_action('wp_ajax_nopriv_spwdprot_get_modal_content', [$this, 'ajaxGetModalContent']); add_action('wp_ajax_spwdprot_get_modal_content', [$this, 'ajaxGetModalContent']); } /** * Plugin activation */ public function activate(): void { // Set default options add_option('spwdprot_password_hash', ''); add_option('spwdprot_impressum_page', ''); add_option('spwdprot_privacy_page', ''); add_option('spwdprot_logo_url', ''); add_option('spwdprot_logo_shape', 'square'); add_option('spwdprot_page_title', __('Password Protected', 'simple-password-protect')); add_option('spwdprot_login_text', __('Please enter the password to access this site.', 'simple-password-protect')); add_option('spwdprot_plugin_enabled', true); add_option('spwdprot_background_color', '#ffffff'); add_option('spwdprot_button_color', '#17525b'); add_option('spwdprot_button_text_color', '#ffffff'); add_option('spwdprot_link_color', '#17525b'); add_option('spwdprot_show_admin_login', false); add_option('spwdprot_show_legal_links', true); } /** * Plugin deactivation */ public function deactivate(): void { // Clear authentication cookies if (isset($_COOKIE[self::COOKIE_NAME])) { setcookie(self::COOKIE_NAME, '', time() - 3600, '/'); } } /** * Initialize plugin */ public function init(): void { // Initialize admin settings if in admin area if (is_admin()) { Spwdprot_Admin::getInstance(); } // Initialize frontend protection (not needed as we use direct implementation now) // if (class_exists('Spwdprot_Frontend')) { // Spwdprot_Frontend::getInstance(); // } } /** * Enqueue frontend scripts and styles */ public function enqueueScripts(): void { if ($this->shouldShowPasswordForm()) { wp_enqueue_style( 'spwdprot-frontend-style', SPWDPROT_PLUGIN_URL . 'assets/css/frontend.css', [], SPWDPROT_VERSION ); wp_enqueue_script( 'spwdprot-frontend-script', SPWDPROT_PLUGIN_URL . 'assets/js/frontend.js', [], SPWDPROT_VERSION, true ); // Localize script for AJAX wp_localize_script('spwdprot-frontend-script', 'spwdprotAjax', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('spwdprot_ajax_nonce'), 'messages' => [ 'invalidPassword' => __('Invalid password. Please try again.', 'simple-password-protect'), 'loadingError' => __('Error loading content. Please try again.', 'simple-password-protect'), ] ]); // Add dynamic styles for customizable colors $this->addDynamicStyles(); // Add inline script for modal functionality $this->addModalScript(); } } /** * Add dynamic styles for customizable colors and admin settings */ private function addDynamicStyles(): void { $backgroundColor = get_option('spwdprot_background_color', '#ffffff'); $buttonColor = get_option('spwdprot_button_color', '#17525b'); $buttonTextColor = get_option('spwdprot_button_text_color', '#ffffff'); $linkColor = get_option('spwdprot_link_color', '#17525b'); $custom_css = " /* Admin customizable colors */ body.spwdprot-body { background: " . esc_attr($backgroundColor) . " !important; background-image: none !important; } .spp-button { background: " . esc_attr($buttonColor) . " !important; background-image: none !important; color: " . esc_attr($buttonTextColor) . " !important; } .spp-button:hover { background: " . esc_attr($buttonColor) . " !important; background-image: none !important; opacity: 0.9; transform: translateY(-2px); } .spp-button:disabled { background: #94a3b8 !important; color: #ffffff !important; opacity: 0.7; transform: none; } .spp-legal-link, .spp-modal-body a { color: " . esc_attr($linkColor) . " !important; } .spp-legal-link:hover { color: " . esc_attr($linkColor) . " !important; opacity: 0.8; text-decoration: underline; } /* Enhanced modal centering and styling */ .spp-modal { z-index: 999999 !important; } .spp-modal.show { display: flex !important; align-items: center !important; justify-content: center !important; } .spp-modal-content { margin: auto !important; transform: translate3d(0, 0, 0); } /* Loading spinner styling */ .spp-loading { border-top-color: " . esc_attr($buttonColor) . " !important; } /* Enhanced accessibility and focus states */ .spp-input:focus { border-color: " . esc_attr($buttonColor) . " !important; box-shadow: 0 0 0 3px " . esc_attr($buttonColor) . "20 !important; } .spp-modal-close:focus { outline: 2px solid " . esc_attr($buttonColor) . " !important; outline-offset: 2px; } /* Admin login button styling */ .spp-admin-login-container { margin-bottom: 20px; } .spp-admin-login-button { display: block; width: 100%; padding: 9px 18px; background: transparent !important; color: " . esc_attr($linkColor) . " !important; border: 2px solid " . esc_attr($linkColor) . " !important; border-radius: 8px; text-decoration: none !important; font-size: 16px; font-weight: 600; transition: all 0.3s ease; text-align: center; box-sizing: border-box; line-height: 1.5; } .spp-admin-login-button:hover { background: " . esc_attr($linkColor) . " !important; color: #ffffff !important; transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); } .spp-admin-login-button:active { transform: translateY(0); } "; wp_add_inline_style('spwdprot-frontend-style', $custom_css); } /** * Add modal functionality script */ private function addModalScript(): void { $script = " function openModal(pageId) { const modal = document.getElementById('spp-modal'); const modalHeader = document.getElementById('spp-modal-header'); const modalTitle = document.getElementById('spp-modal-title'); const modalBody = document.getElementById('spp-modal-body'); if (!modal) return; // Show modal with proper centering modal.classList.add('show'); document.body.style.overflow = 'hidden'; // Set loading state if (modalTitle) modalTitle.textContent = 'Loading...'; if (modalBody) modalBody.innerHTML = '<div class=\"spp-modal-loading\"><div class=\"spp-loading\"></div><span class=\"spp-loading-text\">Loading content...</span></div>'; if (modalHeader) modalHeader.style.display = 'block'; // Fetch page content const formData = new FormData(); formData.append('action', 'spwdprot_get_modal_content'); formData.append('page_id', pageId); formData.append('nonce', '" . esc_js(wp_create_nonce('spwdprot_ajax_nonce')) . "'); fetch('" . esc_url(admin_url('admin-ajax.php')) . "', { method: 'POST', body: formData, credentials: 'same-origin' }) .then(response => response.json()) .then(data => { if (data.success && data.data) { if (modalTitle) modalTitle.textContent = data.data.title || 'Content'; if (modalBody) modalBody.innerHTML = '<div class=\"entry-content\">' + (data.data.content || '<p>No content available.</p>') + '</div>'; } else { if (modalTitle) modalTitle.textContent = 'Error'; if (modalBody) modalBody.innerHTML = '<p style=\"color: #d63638; text-align: center;\">Error loading content. Please try again.</p>'; } }) .catch(error => { console.error('Modal content loading error:', error); if (modalTitle) modalTitle.textContent = 'Error'; if (modalBody) modalBody.innerHTML = '<p style=\"color: #d63638; text-align: center;\">Error loading content. Please try again.</p>'; }); } function closeModal() { const modal = document.getElementById('spp-modal'); if (!modal) return; modal.classList.remove('show'); document.body.style.overflow = ''; // Clear content after animation setTimeout(function() { const modalTitle = document.getElementById('spp-modal-title'); const modalBody = document.getElementById('spp-modal-body'); if (modalTitle) modalTitle.textContent = ''; if (modalBody) modalBody.innerHTML = ''; }, 300); } // Close modal when clicking outside of it document.addEventListener('click', function(event) { const modal = document.getElementById('spp-modal'); if (event.target === modal) { closeModal(); } }); // Close modal with Escape key document.addEventListener('keydown', function(event) { if (event.key === 'Escape') { const modal = document.getElementById('spp-modal'); if (modal && modal.classList.contains('show')) { closeModal(); } } }); "; wp_add_inline_script('spwdprot-frontend-script', $script); } /** * Enqueue admin scripts and styles */ public function enqueueAdminScripts(string $hook): void { if ($hook === 'settings_page_simple-password-protect') { wp_enqueue_media(); wp_enqueue_script( 'spwdprot-admin-script', SPWDPROT_PLUGIN_URL . 'assets/js/admin.js', ['jquery'], SPWDPROT_VERSION, true ); wp_enqueue_style( 'spwdprot-admin-style', SPWDPROT_PLUGIN_URL . 'assets/css/admin.css', [], SPWDPROT_VERSION ); // Add admin inline styles $admin_styles = " .spp-status-info { background: #f1f1f1; border-left: 4px solid #72aee6; padding: 12px; margin: 20px 0; } .spp-status-info h3 { margin-top: 0; } .spp-logo-preview { max-width: 200px; max-height: 100px; margin-top: 10px; border: 1px solid #ddd; padding: 5px; } .spp-logo-preview.round { border-radius: 50%; object-fit: cover; width: 100px; height: 100px; } .spp-logo-preview.square { border-radius: 4px; } .spp-logo-preview.rounded { border-radius: 12px; } "; wp_add_inline_style('spwdprot-admin-style', $admin_styles); // Add admin inline scripts $admin_script = " document.addEventListener('DOMContentLoaded', function() { // Update logo preview when shape changes const shapeSelect = document.getElementById('spwdprot_logo_shape'); const logoPreview = document.getElementById('spwdprot-logo-preview'); if (shapeSelect && logoPreview) { shapeSelect.addEventListener('change', function() { logoPreview.className = 'spp-logo-preview ' + this.value; }); } // Color picker synchronization const colorFields = [ 'spwdprot_background_color', 'spwdprot_button_color', 'spwdprot_button_text_color', 'spwdprot_link_color' ]; colorFields.forEach(function(fieldId) { const colorInput = document.getElementById(fieldId); const textInput = document.getElementById(fieldId + '_text'); if (colorInput && textInput) { colorInput.addEventListener('change', function() { textInput.value = this.value; }); } }); }); "; wp_add_inline_script('spwdprot-admin-script', $admin_script); } } /** * Check if password protection should be active */ public function checkPasswordProtection(): void { // Ensure WordPress is properly loaded if (!did_action('wp_loaded')) { return; } // Skip if user is logged in or plugin is disabled if (is_user_logged_in() || !get_option('spwdprot_plugin_enabled', true)) { return; } // Skip if in admin area or login page $pagenow = $GLOBALS['pagenow'] ?? ''; if (is_admin() || $pagenow === 'wp-login.php') { return; } // Skip AJAX requests if (wp_doing_ajax()) { return; } // Check if user is already authenticated if ($this->isAuthenticated()) { return; } // Show password protection form $this->showPasswordForm(); exit; } /** * Check if user is authenticated via cookie */ private function isAuthenticated(): bool { if (!isset($_COOKIE[self::COOKIE_NAME])) { return false; } $cookieValue = sanitize_text_field(wp_unslash($_COOKIE[self::COOKIE_NAME])); $expectedValue = $this->generateAuthToken(); return hash_equals($expectedValue, $cookieValue); } /** * Generate authentication token */ private function generateAuthToken(): string { $salt = wp_salt('auth'); return hash('sha256', $salt . get_option('spwdprot_password_hash', '')); } /** * Set authentication cookie */ private function setAuthCookie(): void { try { $token = $this->generateAuthToken(); $cookie_set = setcookie( self::COOKIE_NAME, $token, time() + self::COOKIE_EXPIRY, '/', '', is_ssl(), true ); if (!$cookie_set) { $this->logError('Failed to set authentication cookie'); } } catch (Exception $e) { $this->logError('Cookie setting failed: ' . $e->getMessage()); } } /** * Check if password form should be shown */ private function shouldShowPasswordForm(): bool { $pagenow = $GLOBALS['pagenow'] ?? ''; return !is_user_logged_in() && get_option('spwdprot_plugin_enabled', true) && !is_admin() && $pagenow !== 'wp-login.php' && !wp_doing_ajax() && !$this->isAuthenticated(); } /** * Show password protection form */ private function showPasswordForm(): void { // Simple direct implementation to avoid dependency issues $this->displaySimplePasswordForm(); } /** * Display a simple password form directly */ private function displaySimplePasswordForm(): void { // Process form submission first with proper nonce verification if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['spwdprot_password'])) { // Verify nonce before processing $nonce = isset($_POST['spwdprot_nonce']) ? sanitize_text_field(wp_unslash($_POST['spwdprot_nonce'])) : ''; if (wp_verify_nonce($nonce, 'spwdprot_password_form')) { $this->processDirectPasswordSubmission(); } // If nonce verification fails, form will be displayed again } // Set headers header('Cache-Control: no-cache, no-store, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); // Get current URL safely $currentUrl = $this->getCurrentUrlSafe(); // Get settings for GDPR links and appearance $logoUrl = get_option('spwdprot_logo_url', ''); $logoShape = get_option('spwdprot_logo_shape', 'square'); $pageTitle = get_option('spwdprot_page_title', 'Password Protected'); $loginText = get_option('spwdprot_login_text', 'Please enter the password to access this site.'); $impressumPageId = get_option('spwdprot_impressum_page', 0); $privacyPageId = get_option('spwdprot_privacy_page', 0); ?> <!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta charset="<?php bloginfo('charset'); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="robots" content="noindex, nofollow"> <title><?php echo esc_html($pageTitle . ' - ' . get_bloginfo('name')); ?></title> <?php wp_head(); ?> </head> <body class="spwdprot-body"> <div class="spp-container"> <?php if ($logoUrl): ?> <div class="spp-logo-container"> <?php // Check if logo URL is a WordPress attachment $attachment_id = attachment_url_to_postid($logoUrl); if ($attachment_id) { // Use WordPress function for attachments echo wp_get_attachment_image($attachment_id, 'medium', false, [ 'class' => 'spp-logo ' . esc_attr($logoShape), 'alt' => get_bloginfo('name') . ' Logo' ]); } else { // Check if it might be a WordPress media URL without attachment_id $upload_dir = wp_upload_dir(); if (strpos($logoUrl, $upload_dir['baseurl']) !== false) { // Try to get attachment by URL $attachment_id = attachment_url_to_postid($logoUrl); if ($attachment_id) { echo wp_get_attachment_image($attachment_id, 'medium', false, [ 'class' => 'spp-logo ' . esc_attr($logoShape), 'alt' => get_bloginfo('name') . ' Logo' ]); } else { // Fallback: Create img element using WordPress standards $this->renderFallbackImage($logoUrl, get_bloginfo('name') . ' Logo', 'spp-logo ' . esc_attr($logoShape)); } } else { // External URL fallback: Create img element using WordPress standards $this->renderFallbackImage($logoUrl, get_bloginfo('name') . ' Logo', 'spp-logo ' . esc_attr($logoShape)); } } ?> </div> <?php endif; ?> <h1 class="spp-title"><?php echo esc_html($pageTitle); ?></h1> <p class="spp-text"><?php echo esc_html($loginText); ?></p> <?php if (isset($_GET['error'])): ?> <div class="spp-error">Invalid password. Please try again.</div> <?php endif; ?> <form method="post" action=""> <input type="password" name="spwdprot_password" class="spp-input" placeholder="Enter password" required> <input type="hidden" name="redirect_url" value="<?php echo esc_attr($currentUrl); ?>"> <input type="hidden" name="spwdprot_nonce" value="<?php echo esc_attr(wp_create_nonce('spwdprot_password_form')); ?>"> <button type="submit" class="spp-button">Enter</button> </form> <?php if (get_option('spwdprot_show_legal_links', true) && ($impressumPageId || $privacyPageId)): ?> <div class="spp-legal-links"> <?php if (get_option('spwdprot_show_admin_login', false)): ?> <div class="spp-admin-login-container"> <a href="<?php echo esc_url(wp_login_url()); ?>" class="spp-admin-login-button"> <?php esc_html_e('Admin Login', 'simple-password-protect'); ?> </a> </div> <?php endif; ?> <?php if ($impressumPageId): ?> <?php $impressumPage = get_post($impressumPageId); $impressumTitle = $impressumPage ? $impressumPage->post_title : __('Legal Disclosure', 'simple-password-protect'); ?> <a href="#" onclick="openModal(<?php echo intval($impressumPageId); ?>); return false;" class="spp-legal-link"> <?php echo esc_html($impressumTitle); ?> </a> <?php endif; ?> <?php if ($impressumPageId && $privacyPageId): ?> <span class="spp-legal-separator">|</span> <?php endif; ?> <?php if ($privacyPageId): ?> <?php $privacyPage = get_post($privacyPageId); $privacyTitle = $privacyPage ? $privacyPage->post_title : __('Privacy Policy', 'simple-password-protect'); ?> <a href="#" onclick="openModal(<?php echo intval($privacyPageId); ?>); return false;" class="spp-legal-link"> <?php echo esc_html($privacyTitle); ?> </a> <?php endif; ?> </div> <?php elseif (get_option('spwdprot_show_admin_login', false)): ?> <!-- Show admin login button even if legal links are hidden --> <div class="spp-legal-links"> <div class="spp-admin-login-container"> <a href="<?php echo esc_url(wp_login_url()); ?>" class="spp-admin-login-button"> <?php esc_html_e('Admin Login', 'simple-password-protect'); ?> </a> </div> </div> <?php endif; ?> </div> <!-- Modal for legal pages --> <div id="spp-modal" class="spp-modal"> <div class="spp-modal-content"> <div id="spp-modal-header" class="spp-modal-header"> <h2 id="spp-modal-title" class="spp-modal-title">Loading...</h2> <button type="button" class="spp-modal-close" onclick="closeModal()" aria-label="Close">×</button> </div> <div id="spp-modal-body" class="spp-modal-body"> <div style="text-align: center; padding: 20px;"> <div class="spp-loading"></div> Loading content... </div> </div> </div> </div> <?php wp_footer(); ?> </body> </html> <?php } /** * Process direct password submission */ private function processDirectPasswordSubmission(): void { // Verify nonce $nonce = isset($_POST['spwdprot_nonce']) ? sanitize_text_field(wp_unslash($_POST['spwdprot_nonce'])) : ''; if (!wp_verify_nonce($nonce, 'spwdprot_password_form')) { $this->logError('Nonce verification failed'); $this->redirectWithError(); return; } // Get password exactly as submitted - intentionally not sanitized to preserve password characters // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash $password = isset($_POST['spwdprot_password']) ? $_POST['spwdprot_password'] : ''; $passwordHash = get_option('spwdprot_password_hash', ''); // Debug logging $this->logError('Password verification attempt - Password length: ' . strlen($password) . ', Hash exists: ' . (!empty($passwordHash) ? 'yes' : 'no')); // Validate inputs if (empty($password)) { $this->logError('Empty password submitted'); $this->redirectWithError(); return; } if (empty($passwordHash)) { $this->logError('No password hash configured'); $this->redirectWithError(); return; } // Verify password with proper error handling try { $verifyResult = password_verify($password, $passwordHash); $this->logError('Password verify result: ' . ($verifyResult ? 'SUCCESS' : 'FAILED')); if (!$verifyResult) { $this->logError('Invalid password attempt - Hash format: ' . substr($passwordHash, 0, 10) . '...'); $this->redirectWithError(); return; } } catch (Exception $e) { $this->logError('Password verification exception: ' . $e->getMessage()); $this->redirectWithError(); return; } // Set authentication cookie $this->setAuthCookie(); // Redirect $redirectUrl = isset($_POST['redirect_url']) ? sanitize_url(wp_unslash($_POST['redirect_url'])) : home_url(); if (function_exists('wp_redirect')) { wp_redirect($redirectUrl); } else { header('Location: ' . $redirectUrl); } exit; } /** * Redirect with error parameter */ private function redirectWithError(): void { $currentUrl = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '/'; $separator = strpos($currentUrl, '?') !== false ? '&' : '?'; $redirectUrl = $currentUrl . $separator . 'error=1'; if (function_exists('wp_redirect')) { wp_redirect($redirectUrl); } else { header('Location: ' . $redirectUrl); } exit; } /** * Render fallback image using WordPress standards */ private function renderFallbackImage(string $src, string $alt, string $class): void { // Use WordPress HTML API if available (WordPress 6.2+) if (class_exists('WP_HTML_Tag_Processor')) { $processor = new WP_HTML_Tag_Processor('<img>'); if ($processor->next_tag('img')) { $processor->set_attribute('src', $src); $processor->set_attribute('alt', $alt); $processor->set_attribute('class', $class); echo wp_kses($processor->get_updated_html(), [ 'img' => [ 'src' => [], 'alt' => [], 'class' => [] ] ]); return; } } // Fallback: Build attributes array and use wp_kses $attributes = [ 'src' => esc_url($src), 'alt' => esc_attr($alt), 'class' => esc_attr($class) ]; $img_html = '<img'; foreach ($attributes as $key => $value) { $img_html .= ' ' . $key . '="' . $value . '"'; } $img_html .= ' />'; // Use wp_kses to ensure the HTML is safe echo wp_kses($img_html, [ 'img' => [ 'src' => [], 'alt' => [], 'class' => [] ] ]); } /** * Get current URL safely */ private function getCurrentUrlSafe(): string { $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://'; $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : 'localhost'; $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '/'; return $protocol . $host . $uri; } /** * AJAX handler for password verification */ public function ajaxVerifyPassword(): void { // Check if this is a POST request if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') { wp_send_json_error(__('Invalid request method.', 'simple-password-protect')); } // Verify nonce $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''; if (!wp_verify_nonce($nonce, 'spwdprot_ajax_nonce')) { wp_send_json_error(__('Security check failed.', 'simple-password-protect')); } // Get password exactly as submitted - intentionally not sanitized to preserve password characters // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash $password = isset($_POST['password']) ? $_POST['password'] : ''; if (empty($password)) { $this->logError('AJAX: Empty password submitted'); wp_send_json_error(__('Password is required.', 'simple-password-protect')); } $storedHash = get_option('spwdprot_password_hash', ''); if (empty($storedHash)) { $this->logError('AJAX: No password hash configured'); wp_send_json_error(__('No password configured.', 'simple-password-protect')); } try { $verifyResult = password_verify($password, $storedHash); $this->logError('AJAX: Password verify result: ' . ($verifyResult ? 'SUCCESS' : 'FAILED')); if ($verifyResult) { $this->setAuthCookie(); wp_send_json_success(__('Password correct. Redirecting...', 'simple-password-protect')); } else { $this->logError('AJAX: Invalid password attempt - Password length: ' . strlen($password)); wp_send_json_error(__('Invalid password.', 'simple-password-protect')); } } catch (Exception $e) { $this->logError('AJAX: Password verification exception: ' . $e->getMessage()); wp_send_json_error(__('Authentication error. Please try again.', 'simple-password-protect')); } } /** * AJAX handler for modal content */ public function ajaxGetModalContent(): void { // Check if this is a POST request if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') { wp_send_json_error(__('Invalid request method.', 'simple-password-protect')); } // Verify nonce $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''; if (!wp_verify_nonce($nonce, 'spwdprot_ajax_nonce')) { wp_send_json_error(__('Security check failed.', 'simple-password-protect')); } $pageId = isset($_POST['page_id']) ? absint($_POST['page_id']) : 0; if (!$pageId) { wp_send_json_error(__('Invalid page ID.', 'simple-password-protect')); } $page = get_post($pageId); if (!$page || $page->post_status !== 'publish') { wp_send_json_error(__('Page not found.', 'simple-password-protect')); } $content = apply_filters('the_content', $page->post_content); wp_send_json_success([ 'title' => $page->post_title, 'content' => $content ]); } } /** * Initialize the plugin */ function spwdprot_init(): Spwdprot_Main { return Spwdprot_Main::getInstance(); } // Start the plugin spwdprot_init();
Save File
Cancel