From de376d04d124b6b9898704a3037140982cfac3ea Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 23 Mar 2025 23:13:29 -0700 Subject: [PATCH 01/24] Initial commit --- src/RaspAP/Tokens/CSRFTokenizer.php | 114 ++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/RaspAP/Tokens/CSRFTokenizer.php diff --git a/src/RaspAP/Tokens/CSRFTokenizer.php b/src/RaspAP/Tokens/CSRFTokenizer.php new file mode 100644 index 00000000..b86e2f6a --- /dev/null +++ b/src/RaspAP/Tokens/CSRFTokenizer.php @@ -0,0 +1,114 @@ + + * @author Martin Glaß + * @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE + */ + +declare(strict_types=1); + +namespace RaspAP\Tokens; + +class CSRFTokenizer +{ + + // Constructor + public function __construct() + { + $this->ensureSession(); + if ($this->csrfValidateRequest() && !$this->CSRFValidate()) { + $this->handleInvalidCSRFToken(); + } + } + + /** + * Saves a CSRF token in the session + */ + public function ensureCSRFSessionToken(): void + { + if (empty($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); + } + } + + /** + * Add CSRF Token to form + */ + public function CSRFTokenFieldTag(): string + { + $token = htmlspecialchars($_SESSION['csrf_token']); + return ''; + } + + /** + * Returns a CSRF meta tag (for use with xhr, for example) + */ + public function CSRFMetaTag(): string + { + $token = htmlspecialchars($_SESSION['csrf_token']); + return ''; + } + + /** + * Validates a CSRF Token + * + * @param string $token + */ + public function CSRFValidate(string $token): bool + { + if(isset($token) { + $header_token = $_SERVER['HTTP_X_CSRF_TOKEN']; + + if (empty($token) && empty($header_token)) { + return false; + } + $request_token = $token; + if (empty($token)) { + $request_token = $header_token; + } + if (hash_equals($_SESSION['csrf_token'], $request_token)) { + return true; + } else { + error_log('CSRF violation'); + return false; + } + } + } + + /** + * Should the request be CSRF-validated? + */ + public function csrfValidateRequest(): string + { + $request_method = strtolower($_SERVER['REQUEST_METHOD']); + return in_array($request_method, [ "post", "put", "patch", "delete" ]); + } + + /** + * Handle invalid CSRF + */ + public function handleInvalidCSRFToken(): string + { + if (function_exists('http_response_code')) { + http_response_code(500); + echo 'Invalid CSRF token'; + } else { + header('HTTP/1.1 500 Internal Server Error'); + header('Content-Type: text/plain'); + echo 'Invalid CSRF token'; + } + exit; + } + + protected function ensureSession() + { + if (session_status() == PHP_SESSION_NONE) { + session_start(); + } + } +} + From c8b0408bd55dbce0681209a6eb1fbf1330ab0661 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Mar 2025 05:14:29 -0700 Subject: [PATCH 02/24] Refactor + fixup autoloader --- includes/autoload.php | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/includes/autoload.php b/includes/autoload.php index d39bd0c9..87177471 100755 --- a/includes/autoload.php +++ b/includes/autoload.php @@ -9,31 +9,13 @@ */ spl_autoload_register(function ($class) { - // project-specific namespace prefix - $prefix = ''; + // base directory where all class files are stored + $base_dir = __DIR__ . '/../src/'; - // base directory for the namespace prefix - $base_dir = 'src/'; + // convert the fully qualified class name into a file path + $file = $base_dir . str_replace('\\', '/', $class) . '.php'; - // normalize the base directory with a trailing separator - $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/'; - - // does the class use the namespace prefix? - $len = strlen($prefix); - if (strncmp($prefix, $class, $len) !== 0) { - // no, move to the next registered autoloader - return; - } - - // get the relative class name - $relative_class = substr($class, $len); - - // replace the namespace prefix with the base directory, replace namespace - // separators with directory separators in the relative class name, append - // with .php - $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; - - // if the file exists, require it + // require the file if it exists if (file_exists($file)) { require $file; } From 2a70f6ee1189be3e22eae594eb185d69d191c50f Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Mar 2025 05:15:34 -0700 Subject: [PATCH 03/24] Replace csrf and explicit HTTPAuth includes with autoload.php --- ajax/adblock/update_blocklist.php | 4 +--- ajax/bandwidth/get_bandwidth.php | 4 +--- ajax/bandwidth/get_bandwidth_hourly.php | 4 +--- ajax/logging/clearlog.php | 4 +--- ajax/networking/do_sys_reset.php | 10 +++++----- ajax/networking/get_all_interfaces.php | 4 +--- ajax/networking/get_channel.php | 4 +--- ajax/networking/get_frequencies.php | 5 +---- ajax/networking/get_ip_summary.php | 4 +--- ajax/networking/get_netcfg.php | 5 ++--- ajax/networking/get_nl80211_band.php | 4 +--- ajax/networking/get_wgcfg.php | 4 +--- ajax/networking/get_wgkey.php | 4 +--- ajax/networking/wifi_stations.php | 4 +--- ajax/openvpn/activate_ovpncfg.php | 4 +--- ajax/openvpn/del_ovpncfg.php | 4 +--- ajax/plugins/do_plugin_install.php | 5 +---- ajax/session/do_check_session.php | 4 +--- 18 files changed, 23 insertions(+), 58 deletions(-) diff --git a/ajax/adblock/update_blocklist.php b/ajax/adblock/update_blocklist.php index 46f7798e..f21cb4aa 100644 --- a/ajax/adblock/update_blocklist.php +++ b/ajax/adblock/update_blocklist.php @@ -1,9 +1,7 @@ csrfValidateRequest() && !$token->CSRFValidate()) { + $token->handleInvalidCSRFToken(); } $return = 0; $path = "../../config"; @@ -33,5 +32,6 @@ if (isset($_POST['csrf_token'])) { echo json_encode($jsonData); } else { - handleInvalidCSRFToken(); + $token->handleInvalidCSRFToken(); } + diff --git a/ajax/networking/get_all_interfaces.php b/ajax/networking/get_all_interfaces.php index b88e987b..2953b734 100644 --- a/ajax/networking/get_all_interfaces.php +++ b/ajax/networking/get_all_interfaces.php @@ -1,9 +1,7 @@ Date: Tue, 25 Mar 2025 05:17:58 -0700 Subject: [PATCH 04/24] Remove CSRF related functions (made obsolete by Token class) --- includes/functions.php | 76 ------------------------------------------ 1 file changed, 76 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 54b4939c..6ef53ea4 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -306,82 +306,6 @@ function filter_comments($var) return $var[0] != '#'; } -/** - * Saves a CSRF token in the session - */ -function ensureCSRFSessionToken() -{ - if (empty($_SESSION['csrf_token'])) { - $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); - } -} - -/** - * Add CSRF Token to form - */ -function CSRFTokenFieldTag() -{ - $token = htmlspecialchars($_SESSION['csrf_token']); - return ''; -} - -/** - * Retuns a CSRF meta tag (for use with xhr, for example) - */ -function CSRFMetaTag() -{ - $token = htmlspecialchars($_SESSION['csrf_token']); - return ''; -} - -/** - * Validate CSRF Token - */ -function CSRFValidate() -{ - if (empty($_SESSION['csrf_token']) || !is_string($_SESSION['csrf_token'])) { - error_log('Session expired or CSRF token is missing.'); - header('Location: /login'); - exit; - } - - $post_token = $_POST['csrf_token'] ?? null; - $header_token = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null; - - if (empty($post_token) && is_null($header_token)) { - error_log('CSRF token missing in the request'); - return false; - } - $request_token = $post_token ?: $header_token; - - if (hash_equals($_SESSION['csrf_token'], $request_token)) { - return true; - } else { - error_log('CSRF token mismatch'); - return false; - } -} - -/** - * Should the request be CSRF-validated? - */ -function csrfValidateRequest() -{ - $request_method = strtolower($_SERVER['REQUEST_METHOD']); - return in_array($request_method, [ "post", "put", "patch", "delete" ]); -} - -/** - * Handle invalid CSRF - */ -function handleInvalidCSRFToken() -{ - header('HTTP/1.1 500 Internal Server Error'); - header('Content-Type: text/plain'); - echo 'Invalid CSRF token'; - exit; -} - /** * Test whether array is associative */ From a5907d8f7f2acac17875794b6d57915748a75794 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Mar 2025 05:21:35 -0700 Subject: [PATCH 05/24] Class loading handled by autoloader, objects instantiated by index.php --- includes/csrf.php | 7 ------- includes/exceptions.php | 6 ------ 2 files changed, 13 deletions(-) delete mode 100755 includes/csrf.php delete mode 100755 includes/exceptions.php diff --git a/includes/csrf.php b/includes/csrf.php deleted file mode 100755 index 4e098e5b..00000000 --- a/includes/csrf.php +++ /dev/null @@ -1,7 +0,0 @@ - - From b3c61782749559ae710b2bcc608ecf6f000650f5 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Mar 2025 05:22:29 -0700 Subject: [PATCH 06/24] Accept $token object, pass to renderTemplate() --- includes/about.php | 5 +++-- includes/adblock.php | 6 ++--- includes/admin.php | 5 +++-- includes/configure_client.php | 11 ++++++++-- includes/dashboard.php | 9 ++++---- includes/dhcp.php | 5 +++-- includes/hostapd.php | 5 +++-- includes/login.php | 5 +++-- includes/openvpn.php | 5 +++-- includes/page_actions.php | 41 ++++++++++++++++++----------------- includes/restapi.php | 19 +++++++++------- includes/system.php | 5 +++-- includes/wireguard.php | 5 +++-- 13 files changed, 72 insertions(+), 54 deletions(-) diff --git a/includes/about.php b/includes/about.php index 45008f2d..12bf1897 100755 --- a/includes/about.php +++ b/includes/about.php @@ -5,7 +5,7 @@ require_once "app/lib/Parsedown.php"; /** * Displays info about the RaspAP project */ -function DisplayAbout() +function DisplayAbout($token) { $Parsedown = new Parsedown(); $strContent = file_get_contents($_SERVER['DOCUMENT_ROOT'].'/BACKERS.md'); @@ -17,7 +17,8 @@ function DisplayAbout() echo renderTemplate( "about", compact( 'sponsorsHtml', - 'contributingHtml' + 'contributingHtml', + 'token' ) ); } diff --git a/includes/adblock.php b/includes/adblock.php index ec6fb38d..75a751f0 100755 --- a/includes/adblock.php +++ b/includes/adblock.php @@ -6,7 +6,7 @@ require_once 'config.php'; * Manages ad blocking (dnsmasq) configuration * */ -function DisplayAdBlockConfig() +function DisplayAdBlockConfig($token) { $status = new \RaspAP\Messages\StatusMessage; $enabled = false; @@ -93,7 +93,6 @@ function DisplayAdBlockConfig() $adblock_log = "Unable to open log file"; } $logdata = getLogLimited(RASPI_DHCPCD_LOG, $adblock_log); - echo renderTemplate( "adblock", compact( "status", @@ -102,7 +101,8 @@ function DisplayAdBlockConfig() "enabled", "custom_enabled", "adblock_custom_content", - "logdata" + "logdata", + "token" ) ); } diff --git a/includes/admin.php b/includes/admin.php index 61dd21ed..c169a6f4 100755 --- a/includes/admin.php +++ b/includes/admin.php @@ -1,6 +1,6 @@ getVpnManged($vpn); - } + $vpnManaged = $vpn ? $dashboard->getVpnManaged($vpn) : null; $firewallManaged = $firewallStatus = ""; $firewallInstalled = array_filter($plugins, fn($p) => str_ends_with($p, 'Firewall')) ? true : false; if (!$firewallInstalled) { @@ -122,7 +120,8 @@ function DisplayDashboard(&$extraFooterScripts): void "wirelessActive", "tetheringActive", "cellularActive", - "status" + "status", + "token" ) ); $extraFooterScripts[] = array('src'=>'app/js/dashboardchart.js', 'defer'=>false); diff --git a/includes/dhcp.php b/includes/dhcp.php index fd6a47ac..2986c602 100755 --- a/includes/dhcp.php +++ b/includes/dhcp.php @@ -5,7 +5,7 @@ require_once 'config.php'; /** * Manage DHCP configuration */ -function DisplayDHCPConfig() +function DisplayDHCPConfig($token) { $status = new \RaspAP\Messages\StatusMessage; if (!RASPI_MONITOR_ENABLED) { @@ -74,7 +74,8 @@ function DisplayDHCPConfig() "upstreamServers", "interfaces", "leases", - "logdata" + "logdata", + "token" ) ); } diff --git a/includes/hostapd.php b/includes/hostapd.php index 99b24fe3..6483c425 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -9,7 +9,7 @@ getWifiInterface(); * Initialize hostapd values, display interface * */ -function DisplayHostAPDConfig() +function DisplayHostAPDConfig($token) { $status = new \RaspAP\Messages\StatusMessage; $system = new \RaspAP\System\Sysinfo; @@ -165,7 +165,8 @@ function DisplayHostAPDConfig() "operatingSystem", "selectedHwMode", "countryCodes", - "logdata" + "logdata", + "token" ) ); } diff --git a/includes/login.php b/includes/login.php index e3084a27..742ff6c1 100755 --- a/includes/login.php +++ b/includes/login.php @@ -6,7 +6,7 @@ require_once 'includes/functions.php'; /** * Handler for administrative user login */ -function DisplayLogin() +function DisplayLogin($token) { // initialize auth object $auth = new \RaspAP\Auth\HTTPAuth; @@ -33,7 +33,8 @@ function DisplayLogin() echo renderTemplate( "login", compact( "status", - "redirectUrl" + "redirectUrl", + "token" ) ); } diff --git a/includes/openvpn.php b/includes/openvpn.php index 2b59666b..9c2c4418 100755 --- a/includes/openvpn.php +++ b/includes/openvpn.php @@ -8,7 +8,7 @@ getWifiInterface(); /** * Manage OpenVPN configuration */ -function DisplayOpenVPNConfig() +function DisplayOpenVPNConfig($token) { $status = new \RaspAP\Messages\StatusMessage; if (!RASPI_MONITOR_ENABLED) { @@ -79,7 +79,8 @@ function DisplayOpenVPNConfig() "authUser", "authPassword", "clients", - "conf_default" + "conf_default", + "token" ) ); } diff --git a/includes/page_actions.php b/includes/page_actions.php index c9649792..28606e07 100755 --- a/includes/page_actions.php +++ b/includes/page_actions.php @@ -9,7 +9,7 @@ $page = $_SERVER['PATH_INFO']; // Check if any plugin wants to handle the request if (!$pluginManager->handlePageAction($page)) { // If no plugin is available fall back to core page action handlers - handleCorePageAction($page, $extraFooterScripts); + handleCorePageAction($page, $extraFooterScripts, $token); } /** @@ -17,64 +17,65 @@ if (!$pluginManager->handlePageAction($page)) { * * @param string $page * @param array $extraFooterScripts + * @param object $token * @return void */ -function handleCorePageAction(string $page, array &$extraFooterScripts): void +function handleCorePageAction(string $page, array &$extraFooterScripts, object $token): void { switch ($page) { case "/wlan0_info": - DisplayDashboard($extraFooterScripts); + DisplayDashboard($extraFooterScripts, $token); break; case "/dhcpd_conf": - DisplayDHCPConfig(); + DisplayDHCPConfig($token); break; case "/wpa_conf": - DisplayWPAConfig(); + DisplayWPAConfig($token); break; case "/network_conf": - DisplayNetworkingConfig(); + DisplayNetworkingConfig($token); break; case "/hostapd_conf": - DisplayHostAPDConfig(); + DisplayHostAPDConfig($token); break; case "/adblock_conf": - DisplayAdBlockConfig(); + DisplayAdBlockConfig($token); break; case "/openvpn_conf": - DisplayOpenVPNConfig(); + DisplayOpenVPNConfig($token); break; case "/wg_conf": - DisplayWireGuardConfig(); + DisplayWireGuardConfig($token); break; case "/provider_conf": - DisplayProviderConfig(); + DisplayProviderConfig($token); break; case "/torproxy_conf": - DisplayTorProxyConfig(); + DisplayTorProxyConfig($token); break; case "/auth_conf": - DisplayAuthConfig($_SESSION['user_id']); + DisplayAuthConfig($_SESSION['user_id'], $token); break; case "/save_hostapd_conf": - SaveTORAndVPNConfig(); + SaveTORAndVPNConfig($token); break; case "/data_use": - DisplayDataUsage($extraFooterScripts); + DisplayDataUsage($extraFooterScripts, $token); break; case "/system_info": - DisplaySystem($extraFooterScripts); + DisplaySystem($extraFooterScripts, $token); break; case "/restapi_conf": - DisplayRestAPI(); + DisplayRestAPI($token); break; case "/about": - DisplayAbout(); + DisplayAbout($token); break; case "/login": - DisplayLogin(); + DisplayLogin($token); break; default: - DisplayDashboard($extraFooterScripts); + DisplayDashboard($extraFooterScripts, $token); } } diff --git a/includes/restapi.php b/includes/restapi.php index 4c11cfce..621e390f 100755 --- a/includes/restapi.php +++ b/includes/restapi.php @@ -6,7 +6,7 @@ require_once 'config.php'; /** * Handler for RestAPI settings */ -function DisplayRestAPI() +function DisplayRestAPI($token) { // initialize status object $status = new \RaspAP\Messages\StatusMessage; @@ -59,13 +59,16 @@ function DisplayRestAPI() $docMsg = sprintf(_("RestAPI docs are accessible here %s"),$docUrl, $faicon); } - echo renderTemplate("restapi", compact( - "status", - "apiKey", - "serviceStatus", - "serviceLog", - "docMsg" - )); + echo renderTemplate( + "restapi", compact( + "status", + "apiKey", + "serviceStatus", + "serviceLog", + "docMsg", + "token" + ) + ); } /** diff --git a/includes/system.php b/includes/system.php index d37a5450..addaeea2 100755 --- a/includes/system.php +++ b/includes/system.php @@ -6,7 +6,7 @@ require_once 'config.php'; /** * */ -function DisplaySystem(&$extraFooterScripts) +function DisplaySystem(&$extraFooterScripts, $token) { $status = new \RaspAP\Messages\StatusMessage; $dashboard = new \RaspAP\UI\Dashboard; @@ -146,7 +146,8 @@ function DisplaySystem(&$extraFooterScripts) "themes", "selectedTheme", "logLimit", - "pluginsTable" + "pluginsTable", + "token" )); } diff --git a/includes/wireguard.php b/includes/wireguard.php index f6beb3dd..5a04801a 100755 --- a/includes/wireguard.php +++ b/includes/wireguard.php @@ -5,7 +5,7 @@ require_once 'config.php'; /** * Displays wireguard server & peer configuration */ -function DisplayWireGuardConfig() +function DisplayWireGuardConfig($token) { $status = new \RaspAP\Messages\StatusMessage; $parseFlag = true; @@ -98,7 +98,8 @@ function DisplayWireGuardConfig() "wg_pendpoint", "wg_pallowedips", "wg_pkeepalive", - "wg_log" + "wg_log", + "token" ) ); } From bbf1caf77789a1728abcf7e6d065f858985f6e79 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 25 Mar 2025 05:23:06 -0700 Subject: [PATCH 07/24] Update with $token->CSRFTokenFieldTag() --- templates/adblock.php | 2 +- templates/admin.php | 2 +- templates/configure_client.php | 2 +- templates/dashboard.php | 2 +- templates/dhcp.php | 2 +- templates/hostapd.php | 2 +- templates/login.php | 2 +- templates/openvpn.php | 2 +- templates/restapi.php | 2 +- templates/system.php | 2 +- templates/system/advanced.php | 2 +- templates/system/basic.php | 2 +- templates/system/plugins.php | 2 +- templates/system/theme.php | 2 +- templates/system/tools.php | 2 +- templates/wireguard.php | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/templates/adblock.php b/templates/adblock.php index b51d6dd8..19f9c544 100755 --- a/templates/adblock.php +++ b/templates/adblock.php @@ -28,7 +28,7 @@
showMessages(); ?>
- + CSRFTokenFieldTag(); ?>