From 302800b4141ed1d92fe7e99a5410674f94d63e41 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 12 Nov 2019 20:13:24 +0100 Subject: [PATCH] new class Api\Header\Http to handle X-Forwarded-Host and -Schema headers also kope now with multiple comma-separated host-names in X-Forwarded-Host header happening with multiple proxys --- api/src/Egw.php | 5 ++- api/src/Framework.php | 25 +---------- api/src/Header/Http.php | 82 +++++++++++++++++++++++++++++++++++ api/src/Session.php | 2 +- setup/inc/class.setup.inc.php | 2 +- 5 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 api/src/Header/Http.php diff --git a/api/src/Egw.php b/api/src/Egw.php index 9a01d8b76b..e8bf7a8176 100644 --- a/api/src/Egw.php +++ b/api/src/Egw.php @@ -140,8 +140,9 @@ class Egw extends Egw\Base } if (isset($_SERVER['HTTP_X_FORWARDED_HOST']) && $GLOBALS['egw_info']['server']['webserver_url'][0] != '/') { - $GLOBALS['egw_info']['server']['webserver_url'] = ($_SERVER['HTTPS'] ? 'https://' : 'http://'). - $_SERVER['HTTP_X_FORWARDED_HOST'].parse_url($GLOBALS['egw_info']['server']['webserver_url'], PHP_URL_PATH); + $GLOBALS['egw_info']['server']['webserver_url'] = + Header\Http::schema().'://'.Header\Http::host(). + parse_url($GLOBALS['egw_info']['server']['webserver_url'], PHP_URL_PATH); } // if no server timezone set, use date_default_timezone_get() to determine it once diff --git a/api/src/Framework.php b/api/src/Framework.php index f1acbd90d6..233f4f9c27 100644 --- a/api/src/Framework.php +++ b/api/src/Framework.php @@ -189,34 +189,11 @@ abstract class Framework extends Framework\Extra /** * Get a full / externally usable URL from an EGroupware link * - * Code is only used, if the Setup defined webserver_url is only a path! - * - * The following HTTP Headers / $_SERVER variables and EGroupware configuration - * is taken into account to determine if URL should use https schema: - * - $_SERVER['HTTPS'] !== off - * - $GLOBALS['egw_info']['server']['enforce_ssl'] (EGroupware Setup) - * - $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' (X-Forwarded-Proto HTTP header) - * - * Host is determined in the following order / priority: - * 1. $GLOBALS['egw_info']['server']['hostname'] !== 'localhost' (EGroupware Setup) - * 2. $_SERVER['HTTP_X_FORWARDED_HOST'] (X-Forwarded-Host HTTP header) - * 3. $_SERVER['HTTP_HOST'] (Host HTTP header) - * * @param string $link */ static function getUrl($link) { - if ($link[0] === '/') - { - $link = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443 || - !empty($GLOBALS['egw_info']['server']['enforce_ssl']) || $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? - 'https://' : 'http://'). - ($GLOBALS['egw_info']['server']['hostname'] && $GLOBALS['egw_info']['server']['hostname'] !== 'localhost' ? - $GLOBALS['egw_info']['server']['hostname'] : - (isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST'])). - $link; - } - return $link; + return Header\Http::fullUrl($link); } /** diff --git a/api/src/Header/Http.php b/api/src/Header/Http.php new file mode 100644 index 0000000000..72a14bc55d --- /dev/null +++ b/api/src/Header/Http.php @@ -0,0 +1,82 @@ + + * @copyright 2019 by RalfBecker@outdoor-training.de + * @package api + * @subpackage header + */ + +namespace EGroupware\Api\Header; + +/** + * HTTP header handling for host and schema + */ +class Http +{ + /** + * Get host considering X-Forwarded-Host and Host header + * + * Host is determined in the following order / priority: + * 1. $GLOBALS['egw_info']['server']['hostname'] !== 'localhost' (EGroupware Setup) + * 2. $_SERVER['HTTP_X_FORWARDED_HOST'] (X-Forwarded-Host HTTP header) + * 3. $_SERVER['HTTP_HOST'] (Host HTTP header) + * + * @return string + */ + static function host() + { + if (!empty($GLOBALS['egw_info']['server']['hostname']) && $GLOBALS['egw_info']['server']['hostname'] !== 'localhost') + { + $host = $GLOBALS['egw_info']['server']['hostname']; + } + elseif (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) + { + list($host) = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); + } + else + { + $host = $_SERVER['HTTP_HOST']; + } + return $host; + } + + /** + * Get schema considering X-Forwarded-Schema and used schema + * + * The following HTTP Headers / $_SERVER variables and EGroupware configuration + * is taken into account to determine if URL should use https schema: + * - $_SERVER['HTTPS'] !== off + * - $GLOBALS['egw_info']['server']['enforce_ssl'] (EGroupware Setup) + * - $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' (X-Forwarded-Proto HTTP header) + * + * @return string + */ + static function schema() + { + return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || + $_SERVER['SERVER_PORT'] == 443 || + !empty($GLOBALS['egw_info']['server']['enforce_ssl']) || + $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? + 'https' : 'http'; + } + + /** + * Get a full / externally usable URL from an EGroupware link + * + * Code is only used, if $link is only a path (starts with slash) + * + * @param string $link + */ + static function fullUrl($link) + { + if ($link[0] === '/') + { + $link = self::schema().'://'.self::host().$link; + } + return $link; + } +} \ No newline at end of file diff --git a/api/src/Session.php b/api/src/Session.php index 51cbe84429..e3f160af54 100644 --- a/api/src/Session.php +++ b/api/src/Session.php @@ -1650,7 +1650,7 @@ class Session else { // Use HTTP_X_FORWARDED_HOST if set, which is the case behind a none-transparent proxy - self::$cookie_domain = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST']; + self::$cookie_domain = Header\Http::host(); } // remove port from HTTP_HOST $arr = null; diff --git a/setup/inc/class.setup.inc.php b/setup/inc/class.setup.inc.php index 2abcc32d94..500c174730 100644 --- a/setup/inc/class.setup.inc.php +++ b/setup/inc/class.setup.inc.php @@ -157,7 +157,7 @@ class setup static function cookiedomain() { // Use HTTP_X_FORWARDED_HOST if set, which is the case behind a none-transparent proxy - $cookie_domain = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST']; + $cookie_domain = Api\Header\Http::host(); // remove port from HTTP_HOST $arr = null;