VYPR
High severityNVD Advisory· Published Nov 1, 2022· Updated Apr 23, 2025

Service Hostname Discovery Exploitation in phpCAS

CVE-2022-39369

Description

phpCAS is an authentication library that allows PHP applications to easily authenticate users via a Central Authentication Service (CAS) server. The phpCAS library uses HTTP headers to determine the service URL used to validate tickets. This allows an attacker to control the host header and use a valid ticket granted for any authorized service in the same SSO realm (CAS server) to authenticate to the service protected by phpCAS. Depending on the settings of the CAS server service registry in worst case this may be any other service URL (if the allowed URLs are configured to "^(https)://.*") or may be strictly limited to known and authorized services in the same SSO federation if proper URL service validation is applied. This vulnerability may allow an attacker to gain access to a victim's account on a vulnerable CASified service without victim's knowledge, when the victim visits attacker's website while being logged in to the same CAS server. phpCAS 1.6.0 is a major version upgrade that starts enforcing service URL discovery validation, because there is unfortunately no 100% safe default config to use in PHP. Starting this version, it is required to pass in an additional service base URL argument when constructing the client class. For more information, please refer to the upgrading doc. This vulnerability only impacts the CAS client that the phpCAS library protects against. The problematic service URL discovery behavior in phpCAS < 1.6.0 will only be disabled, and thus you are not impacted from it, if the phpCAS configuration has the following setup: 1. phpCAS::setUrl() is called (a reminder that you have to pass in the full URL of the current page, rather than your service base URL), and 2. phpCAS::setCallbackURL() is called, only when the proxy mode is enabled. 3. If your PHP's HTTP header input X-Forwarded-Host, X-Forwarded-Server, Host, X-Forwarded-Proto, X-Forwarded-Protocol is sanitized before reaching PHP (by a reverse proxy, for example), you will not be impacted by this vulnerability either. If your CAS server service registry is configured to only allow known and trusted service URLs the severity of the vulnerability is reduced substantially in its severity since an attacker must be in control of another authorized service. Otherwise, you should upgrade the library to get the safe service discovery behavior.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
apereo/phpcasPackagist
< 1.6.01.6.0

Affected products

1

Patches

1
b759361d904a

Merge pull request from GHSA-8q72-6qq8-xv64

https://github.com/apereo/phpCASPhyOct 31, 2022via ghsa
40 files changed · +789 91
  • docs/ChangeLog+19 12 modified
    @@ -1,11 +1,18 @@
    +Changes in version 1.6.0
    +
    +Bug Fixes:
    + * Introduce required service_name constructor argument to fix
    +   service hostname discovery exploitation vulnerability (Henry Pan)
    + * Set user agent [#421] (Fydon)
    +
     Changes in version 1.5.0
     
     Bug Fixes:
      * Fix undefined variables [#417] (Dawid Polak)
      * Fix client when getting ticket and it's null [#415] (Quentin Belot)
      * Allow autoloader to detect trait_exists() [#394] (Jean-Luc Herren)
      * Use curl_setopt_array instead of loop in CurlRequest [#391] (François Freitag)
    - 
    +
     Improvement:
      * Disable printf when verbosity flag is not set to true [#396] (Michał Kleszczyński)
      * Disabling error printing based on verbosity flag [#393] (Michał Kleszczyński)
    @@ -21,18 +28,18 @@ Bug Fixes:
        * Fix use of deprecated setDebug() in examples [#360] (Joachim Fritschi)
        * Fix session_set_save_handler error [#365] (Joachim Fritschi)
        * Fix wrong server_port documentation [#369] (Joachim Fritschi)
    -      
    +
     Improvement:
        * support samesite cookies attribute (#370) (Mickael)
        * Remove PHP5 support [#366] (Joachim Fritschi)
    - 
    - 
    +
    +
     Changes in version 1.3.9
     
     Bug Fixes:
        * Fix regression of #248: Support of longer session tickets (#349) (Alan Nelson)
        * Fix private call generating php warning and no logout handling (#352) (Julien Gribonvald)
    -   
    +
     Improvement:
        * Add support for logging via a PSR-3 logger [#329] (Jon Dufresne)
        * Improve attribute handling [#317] (Tobias Schiebeck)
    @@ -46,7 +53,7 @@ Changes in version 1.3.8
     
     Bug Fixes:
        * Fix pear package [#297] (Phil Fenstermacher)
    -   
    +
     Improvement:
        * Adding support for PROXY CALLBACK using POST parameters instead of GET [#312]
     
    @@ -57,8 +64,8 @@ Bug Fixes:
     
     Improvement:
        * add method to get list of supported protocols (#293) Julien Boulen
    -   
    -  
    +
    +
     Changes in version 1.3.6
     
     Security Fixes:
    @@ -68,7 +75,7 @@ Bug Fixes:
        * Fix bad condition [#252] (Brice Vercoustre)
        * Hash ticket strings to generate valid-length session-ids [#224, #244, #248] (Adam Franco)
        * Fix "phpCAS" class capitalization in code [#273, #277] (phy25)
    -   
    +
     Improvement:
        * Remove fallback for __autoload [#247] (marinaglancy)
        * More robust check for Windows OS in File.php [#275] (xamount)
    @@ -125,8 +132,8 @@ Improvement:
        * Add time to trace [#158] (cwsterling)
        * Add php5.6 tests, move to faster docker env [#169] (Florian Holzhauer)
        * Introduce a setVerbose() toggle to prevent debug info leaking in production [#152 #147] (Joachim Fritschi)
    - 
    - 
    +
    +
     Changes in version 1.3.3
     Security Fixes:
        * CVE-2014-4172 Urlencode all tickets [#125] (Marvin Addison)
    @@ -140,7 +147,7 @@ Bug Fixes:
        * Fix missing Server_Admin variable for nginex [#121](arianf)
        * Fix error in TypeMismatchException [#123 ](Develle)
        * Fix bug in https test [#126] (Florent Baldino)
    -   
    +
     
     Improvement:
        * Fix grammar of documentation [#61] (frett)
    
  • docs/examples/config.example.php+3 0 modified
    @@ -43,6 +43,9 @@
     // Assumes the cas server is load balanced across multiple hosts
     $cas_real_hosts = array('cas-real-1.example.com', 'cas-real-2.example.com');
     
    +// Client config for the required domain name, should be protocol, hostname and port
    +$client_service_name = 'http://127.0.0.1';
    +
     // Client config for cookie hardening
     $client_domain = '127.0.0.1';
     $client_path = 'phpcas';
    
  • docs/examples/create_pgt_storage_db_table.php+1 1 modified
    @@ -28,7 +28,7 @@
     
     // Dummy client because we need a 'client' object
     $client = new CAS_Client(
    -    CAS_VERSION_2_0, true, $cas_host, $cas_port, $cas_context, false
    +    CAS_VERSION_2_0, true, $cas_host, $cas_port, $cas_context, $client_service_name, false
     );
     
     // Set the torage object
    
  • docs/examples/example_advanced_saml11.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(SAML_VERSION_1_1, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(SAML_VERSION_1_1, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_custom_urls.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_gateway.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_hardening.php+1 1 modified
    @@ -28,7 +28,7 @@
     session_set_cookie_params($client_lifetime, $client_path, $client_domain, $client_secure, $client_httpOnly);
     
     // Initialize phpCAS
    -phpCAS::client(SAML_VERSION_1_1, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(SAML_VERSION_1_1, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_html.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_lang.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_logout.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_no_ssl_cn_validation.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_pgt_storage_db.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_pgt_storage_file.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_proxy_GET.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_proxy_POST.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_proxy_rebroadcast.php+1 1 modified
    @@ -26,7 +26,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_proxy_serviceWeb_chaining.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_proxy_serviceWeb.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_renew.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_service.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_service_POST.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_service_that_proxies.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::proxy(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/examples/example_simple.php+1 1 modified
    @@ -25,7 +25,7 @@
     phpCAS::setVerbose(true);
     
     // Initialize phpCAS
    -phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);
    +phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context, $client_service_name);
     
     // For production use set the CA certificate that is the issuer of the cert
     // on the CAS server and uncomment the line below
    
  • docs/Upgrading+34 0 modified
    @@ -1,3 +1,37 @@
    +################################
    +### Upgrading 1.5.0 -> 1.6.0 ###
    +################################
    +
    +phpCAS now requires an additional service base URL argument when constructing
    +the client class, similar to other CAS client's serverName config. It accepts
    +any argument of:
    +
    +1. A service base URL string. The service URL discovery will always use this
    +   server name (protocol, hostname and port number) without using any external
    +   host names.
    +2. An array of service base URL strings. The service URL discovery will check
    +   against this list before using the auto discovered base URL. If there is no
    +   match, the first base URL in the array will be used as the default. This
    +   option is helpful if your PHP website is accessible through multiple domains
    +   without a canonical name, or through both HTTP and HTTPS.
    +3. A class that implements CAS_ServiceBaseUrl_Interface. If you need to
    +   customize the base URL discovery behavior, you can pass in a class that
    +   implements the interface.
    +
    +For option 1 and 2, protocol, hostname and port should all appear without a
    +trailing slash, e.g. http://example.org:8080. You can omit the default port for
    +the protocol, which means use https://example.org instead of
    +https://example.org:443 (if you use HTTPS).
    +
    +For security reasons, we no longer allow service base URL discovery without an
    +allowed list check by default. For more information, refer to the security
    +advisory.
    +
    +This version also changed the CURL User Agent string that phpCAS uses when
    +sending validation requests to the CAS server. It will appear as phpCAS/1.6.0
    +with the version number reflecting the library version.
    +
    +
     ################################
     ### Upgrading 1.3.3 -> 1.3.4 ###
     ################################
    
  • source/CAS/Client.php+56 53 modified
    @@ -918,6 +918,14 @@ public function getAuthenticationCallerMethod ()
          * @param bool                     $changeSessionID Allow phpCAS to change the session_id
          *                                                  (Single Sign Out/handleLogoutRequests
          *                                                  is based on that change)
    +     * @param string|string[]|CAS_ServiceBaseUrl_Interface
    +     *                                 $service_base_url the base URL (protocol, host and the
    +     *                                                  optional port) of the CAS client; pass
    +     *                                                  in an array to use auto discovery with
    +     *                                                  an allowlist; pass in
    +     *                                                  CAS_ServiceBaseUrl_Interface for custom
    +     *                                                  behavior. Added in 1.6.0. Similar to
    +     *                                                  serverName config in other CAS clients.
          * @param \SessionHandlerInterface $sessionHandler  the session handler
          *
          * @return self a newly created CAS_Client object
    @@ -928,6 +936,7 @@ public function __construct(
             $server_hostname,
             $server_port,
             $server_uri,
    +        $service_base_url,
             $changeSessionID = true,
             \SessionHandlerInterface $sessionHandler = null
         ) {
    @@ -945,6 +954,8 @@ public function __construct(
             if (gettype($changeSessionID) != 'boolean')
                 throw new CAS_TypeMismatchException($changeSessionID, '$changeSessionID', 'boolean');
     
    +        $this->_setServiceBaseUrl($service_base_url);
    +
             if (empty($sessionHandler)) {
                 $sessionHandler = new CAS_Session_PhpSession;
             }
    @@ -1049,7 +1060,7 @@ public function __construct(
     
             if ( $this->_isCallbackMode() ) {
                 //callback mode: check that phpCAS is secured
    -            if ( !$this->_isHttps() ) {
    +            if ( !$this->getServiceBaseUrl()->isHttps() ) {
                     phpCAS::error(
                         'CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server'
                     );
    @@ -2578,8 +2589,7 @@ private function _getCallbackURL()
             // the URL is built when needed only
             if ( empty($this->_callback_url) ) {
                 // remove the ticket if present in the URL
    -            $final_uri = 'https://';
    -            $final_uri .= $this->_getClientUrl();
    +            $final_uri = $this->getServiceBaseUrl()->get();
                 $request_uri = $_SERVER['REQUEST_URI'];
                 $request_uri = preg_replace('/\?.*$/', '', $request_uri);
                 $final_uri .= $request_uri;
    @@ -3947,10 +3957,7 @@ public function getURL()
             // the URL is built when needed only
             if ( empty($this->_url) ) {
                 // remove the ticket if present in the URL
    -            $final_uri = ($this->_isHttps()) ? 'https' : 'http';
    -            $final_uri .= '://';
    -
    -            $final_uri .= $this->_getClientUrl();
    +            $final_uri = $this->getServiceBaseUrl()->get();
                 $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2);
                 $final_uri .= $request_uri[0];
     
    @@ -3987,65 +3994,61 @@ public function setBaseURL($url)
             return $this->_server['base_url'] = $url;
         }
     
    +    /**
    +     * The ServiceBaseUrl object that provides base URL during service URL
    +     * discovery process.
    +     *
    +     * @var CAS_ServiceBaseUrl_Interface
    +     *
    +     * @hideinitializer
    +     */
    +    private $_serviceBaseUrl = null;
     
         /**
    -     * Try to figure out the phpCAS client URL with possible Proxys / Ports etc.
    +     * Answer the CAS_ServiceBaseUrl_Interface object for this client.
          *
    -     * @return string Server URL with domain:port
    +     * @return CAS_ServiceBaseUrl_Interface
          */
    -    private function _getClientUrl()
    +    public function getServiceBaseUrl()
         {
    -        if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
    -            // explode the host list separated by comma and use the first host
    -            $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
    -            // see rfc7239#5.3 and rfc7230#2.7.1: port is in HTTP_X_FORWARDED_HOST if non default
    -            return $hosts[0];
    -        } else if (!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])) {
    -            $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER'];
    -        } else {
    -            if (empty($_SERVER['SERVER_NAME'])) {
    -                $server_url = $_SERVER['HTTP_HOST'];
    -            } else {
    -                $server_url = $_SERVER['SERVER_NAME'];
    -            }
    +        if (empty($this->_serviceBaseUrl)) {
    +            phpCAS::error("ServiceBaseUrl object is not initialized");
             }
    -        if (!strpos($server_url, ':')) {
    -            if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
    -                $server_port = $_SERVER['SERVER_PORT'];
    -            } else {
    -                $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']);
    -                $server_port = $ports[0];
    -            }
    -
    -            if ( ($this->_isHttps() && $server_port!=443)
    -                || (!$this->_isHttps() && $server_port!=80)
    -            ) {
    -                $server_url .= ':';
    -                $server_url .= $server_port;
    -            }
    -        }
    -        return $server_url;
    +        return $this->_serviceBaseUrl;
         }
     
         /**
    -     * This method checks to see if the request is secured via HTTPS
    +     * This method sets the service base URL used during service URL discovery process.
    +     *
    +     * This is required since phpCAS 1.6.0 to protect the integrity of the authentication.
    +     *
    +     * @since phpCAS 1.6.0
    +     *
    +     * @param $name can be any of the following:
    +     *   - A base URL string. The service URL discovery will always use this (protocol,
    +     *     hostname and optional port number) without using any external host names.
    +     *   - An array of base URL strings. The service URL discovery will check against
    +     *     this list before using the auto discovered base URL. If there is no match,
    +     *     the first base URL in the array will be used as the default. This option is
    +     *     helpful if your PHP website is accessible through multiple domains without a
    +     *     canonical name, or through both HTTP and HTTPS.
    +     *   - A class that implements CAS_ServiceBaseUrl_Interface. If you need to customize
    +     *     the base URL discovery behavior, you can pass in a class that implements the
    +     *     interface.
          *
    -     * @return bool true if https, false otherwise
    +     * @return void
          */
    -    private function _isHttps()
    +    private function _setServiceBaseUrl($name)
         {
    -        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
    -            return ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
    -        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) {
    -            return ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
    -        } elseif ( isset($_SERVER['HTTPS'])
    -            && !empty($_SERVER['HTTPS'])
    -            && strcasecmp($_SERVER['HTTPS'], 'off') !== 0
    -        ) {
    -            return true;
    +        if (is_array($name)) {
    +            $this->_serviceBaseUrl = new CAS_ServiceBaseUrl_AllowedListDiscovery($name);
    +        } else if (is_string($name)) {
    +            $this->_serviceBaseUrl = new CAS_ServiceBaseUrl_Static($name);
    +        } else if ($name instanceof CAS_ServiceBaseUrl_Interface) {
    +            $this->_serviceBaseUrl = $name;
    +        } else {
    +            throw new CAS_TypeMismatchException($name, '$name', 'array, string, or CAS_ServiceBaseUrl_Interface object');
             }
    -        return false;
    -
         }
     
         /**
    
  • source/CAS.php+22 4 modified
    @@ -327,6 +327,14 @@ class phpCAS
          * @param string                   $server_hostname the hostname of the CAS server
          * @param int                      $server_port     the port the CAS server is running on
          * @param string                   $server_uri      the URI the CAS server is responding on
    +     * @param string|string[]|CAS_ServiceBaseUrl_Interface
    +     *                                 $service_base_url the base URL (protocol, host and the
    +     *                                                  optional port) of the CAS client; pass
    +     *                                                  in an array to use auto discovery with
    +     *                                                  an allowlist; pass in
    +     *                                                  CAS_ServiceBaseUrl_Interface for custom
    +     *                                                  behavior. Added in 1.6.0. Similar to
    +     *                                                  serverName config in other CAS clients.
          * @param bool                     $changeSessionID Allow phpCAS to change the session_id
          *                                                  (Single Sign Out/handleLogoutRequests
          *                                                  is based on that change)
    @@ -338,7 +346,8 @@ class phpCAS
          * and phpCAS::setDebug()).
          */
         public static function client($server_version, $server_hostname,
    -        $server_port, $server_uri, $changeSessionID = true, \SessionHandlerInterface $sessionHandler = null
    +        $server_port, $server_uri, $service_base_url,
    +        $changeSessionID = true, \SessionHandlerInterface $sessionHandler = null
         ) {
             phpCAS :: traceBegin();
             if (is_object(self::$_PHPCAS_CLIENT)) {
    @@ -357,7 +366,7 @@ public static function client($server_version, $server_hostname,
             // initialize the object $_PHPCAS_CLIENT
             try {
                 self::$_PHPCAS_CLIENT = new CAS_Client(
    -                $server_version, false, $server_hostname, $server_port, $server_uri,
    +                $server_version, false, $server_hostname, $server_port, $server_uri, $service_base_url,
                     $changeSessionID, $sessionHandler
                 );
             } catch (Exception $e) {
    @@ -373,6 +382,14 @@ public static function client($server_version, $server_hostname,
          * @param string                   $server_hostname the hostname of the CAS server
          * @param string                   $server_port     the port the CAS server is running on
          * @param string                   $server_uri      the URI the CAS server is responding on
    +     * @param string|string[]|CAS_ServiceBaseUrl_Interface
    +     *                                 $service_base_url the base URL (protocol, host and the
    +     *                                                  optional port) of the CAS client; pass
    +     *                                                  in an array to use auto discovery with
    +     *                                                  an allowlist; pass in
    +     *                                                  CAS_ServiceBaseUrl_Interface for custom
    +     *                                                  behavior. Added in 1.6.0. Similar to
    +     *                                                  serverName config in other CAS clients.
          * @param bool                     $changeSessionID Allow phpCAS to change the session_id
          *                                                  (Single Sign Out/handleLogoutRequests
          *                                                  is based on that change)
    @@ -384,7 +401,8 @@ public static function client($server_version, $server_hostname,
          * and phpCAS::setDebug()).
          */
         public static function proxy($server_version, $server_hostname,
    -        $server_port, $server_uri, $changeSessionID = true, \SessionHandlerInterface $sessionHandler = null
    +        $server_port, $server_uri, $service_base_url,
    +        $changeSessionID = true, \SessionHandlerInterface $sessionHandler = null
         ) {
             phpCAS :: traceBegin();
             if (is_object(self::$_PHPCAS_CLIENT)) {
    @@ -403,7 +421,7 @@ public static function proxy($server_version, $server_hostname,
             // initialize the object $_PHPCAS_CLIENT
             try {
                 self::$_PHPCAS_CLIENT = new CAS_Client(
    -                $server_version, true, $server_hostname, $server_port, $server_uri,
    +                $server_version, true, $server_hostname, $server_port, $server_uri, $service_base_url,
                     $changeSessionID, $sessionHandler
                 );
             } catch (Exception $e) {
    
  • source/CAS/ServiceBaseUrl/AllowedListDiscovery.php+152 0 added
    @@ -0,0 +1,152 @@
    +<?php
    +
    +/**
    + * Licensed to Jasig under one or more contributor license
    + * agreements. See the NOTICE file distributed with this work for
    + * additional information regarding copyright ownership.
    + *
    + * Jasig licenses this file to you under the Apache License,
    + * Version 2.0 (the "License"); you may not use this file except in
    + * compliance with the License. You may obtain a copy of the License at:
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + *
    + * PHP Version 7
    + *
    + * @file     CAS/ServiceBaseUrl/AllowedListDiscovery.php
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +
    +/**
    + * Class that gets the service base URL of the PHP server by HTTP header
    + * discovery and allowlist check. This is used to generate service URL
    + * and PGT callback URL.
    + *
    + * @class    CAS_ServiceBaseUrl_AllowedListDiscovery
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +class CAS_ServiceBaseUrl_AllowedListDiscovery
    +extends CAS_ServiceBaseUrl_Base
    +{
    +    private $_list = array();
    +
    +    public function __construct($list) {
    +        if (is_array($list)) {
    +            if (count($list) === 0) {
    +                throw new CAS_InvalidArgumentException('$list should not be empty');
    +            }
    +            foreach ($list as $value) {
    +                $this->allow($value);
    +            }
    +        } else {
    +            throw new CAS_TypeMismatchException($list, '$list', 'array');
    +        }
    +    }
    +
    +    /**
    +     * Add a base URL to the allowed list.
    +     *
    +     * @param $url protocol, host name and port to add to the allowed list
    +     *
    +     * @return void
    +     */
    +    public function allow($url)
    +    {
    +        $this->_list[] = $this->removeStandardPort($url);
    +    }
    +
    +    /**
    +     * Check if the server name is allowed by configuration.
    +     *
    +     * @param $name server name to check
    +     *
    +     * @return bool whether the allowed list contains the server name
    +     */
    +    protected function isAllowed($name)
    +    {
    +        return in_array($name, $this->_list);
    +    }
    +
    +    /**
    +     * Discover the server name through HTTP headers.
    +     *
    +     * We read:
    +     * - HTTP header X-Forwarded-Host
    +     * - HTTP header X-Forwarded-Server and X-Forwarded-Port
    +     * - HTTP header Host and SERVER_PORT
    +     * - PHP SERVER_NAME (which can change based on the HTTP server used)
    +     *
    +     * The standard port will be omitted (80 for HTTP, 443 for HTTPS).
    +     *
    +     * @return string the discovered, unsanitized server protocol, hostname and port
    +     */
    +    protected function discover()
    +    {
    +        $isHttps = $this->isHttps();
    +        $protocol = $isHttps ? 'https' : 'http';
    +        $protocol .= '://';
    +        if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
    +            // explode the host list separated by comma and use the first host
    +            $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
    +            // see rfc7239#5.3 and rfc7230#2.7.1: port is in HTTP_X_FORWARDED_HOST if non default
    +            return $protocol . $hosts[0];
    +        } else if (!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])) {
    +            $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER'];
    +        } else {
    +            if (empty($_SERVER['SERVER_NAME'])) {
    +                $server_url = $_SERVER['HTTP_HOST'];
    +            } else {
    +                $server_url = $_SERVER['SERVER_NAME'];
    +            }
    +        }
    +        if (!strpos($server_url, ':')) {
    +            if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
    +                $server_port = $_SERVER['SERVER_PORT'];
    +            } else {
    +                $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']);
    +                $server_port = $ports[0];
    +            }
    +
    +            $server_url .= ':';
    +            $server_url .= $server_port;
    +        }
    +        return $protocol . $server_url;
    +    }
    +
    +    /**
    +     * Get PHP server base URL.
    +     *
    +     * @return string the server protocol, hostname and port
    +     */
    +    public function get()
    +    {
    +        phpCAS::traceBegin();
    +        $result = $this->removeStandardPort($this->discover());
    +        phpCAS::trace("Discovered server base URL: " . $result);
    +        if ($this->isAllowed($result)) {
    +            phpCAS::trace("Server base URL is allowed");
    +            phpCAS::traceEnd(true);
    +        } else {
    +            $result = $this->_list[0];
    +            phpCAS::trace("Server base URL is not allowed, using default: " . $result);
    +            phpCAS::traceEnd(false);
    +        }
    +        return $result;
    +    }
    +}
    
  • source/CAS/ServiceBaseUrl/Base.php+98 0 added
    @@ -0,0 +1,98 @@
    +<?php
    +
    +/**
    + * Licensed to Jasig under one or more contributor license
    + * agreements. See the NOTICE file distributed with this work for
    + * additional information regarding copyright ownership.
    + *
    + * Jasig licenses this file to you under the Apache License,
    + * Version 2.0 (the "License"); you may not use this file except in
    + * compliance with the License. You may obtain a copy of the License at:
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + *
    + * PHP Version 7
    + *
    + * @file     CAS/ServiceBaseUrl/Base.php
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +/**
    + * Base class of CAS/ServiceBaseUrl that implements isHTTPS method.
    + *
    + * @class    CAS_ServiceBaseUrl_Base
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +abstract class CAS_ServiceBaseUrl_Base
    +implements CAS_ServiceBaseUrl_Interface
    +{
    +
    +    /**
    +     * Get PHP server name.
    +     *
    +     * @return string the server hostname and port of the server
    +     */
    +    abstract public function get();
    +
    +    /**
    +     * Check whether HTTPS is used.
    +     *
    +     * This is used to construct the protocol in the URL.
    +     *
    +     * @return bool true if HTTPS is used
    +     */
    +    public function isHttps() {
    +        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
    +            return ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
    +        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) {
    +            return ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
    +        } elseif ( isset($_SERVER['HTTPS'])
    +            && !empty($_SERVER['HTTPS'])
    +            && strcasecmp($_SERVER['HTTPS'], 'off') !== 0
    +        ) {
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    /**
    +     * Remove standard HTTP and HTTPS port for discovery and allowlist input.
    +     *
    +     * @param $url URL as https://domain:port without trailing slash
    +     * @return standardized URL, or the original URL
    +     * @throws CAS_InvalidArgumentException if the URL does not include the protocol
    +     */
    +    protected function removeStandardPort($url) {
    +        if (strpos($url, "://") === false) {
    +            throw new CAS_InvalidArgumentException(
    +                "Configured base URL should include the protocol string: " . $url);
    +        }
    +
    +        $url = rtrim($url, '/');
    +
    +        if (strpos($url, "https://") === 0 && substr_compare($url, ':443', -4) === 0) {
    +            return substr($url, 0, -4);
    +        }
    +
    +        if (strpos($url, "http://") === 0 && substr_compare($url, ':80', -3) === 0) {
    +            return substr($url, 0, -3);
    +        }
    +
    +        return $url;
    +    }
    +
    +}
    
  • source/CAS/ServiceBaseUrl/Interface.php+61 0 added
    @@ -0,0 +1,61 @@
    +<?php
    +
    +/**
    + * Licensed to Jasig under one or more contributor license
    + * agreements. See the NOTICE file distributed with this work for
    + * additional information regarding copyright ownership.
    + *
    + * Jasig licenses this file to you under the Apache License,
    + * Version 2.0 (the "License"); you may not use this file except in
    + * compliance with the License. You may obtain a copy of the License at:
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + *
    + * PHP Version 7
    + *
    + * @file     CAS/ServerHostname/Interface.php
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +/**
    + * An interface for classes that gets the server name of the PHP server.
    + * This is used to generate service URL and PGT callback URL.
    + *
    + * @class    CAS_ServiceBaseUrl_Interface
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +interface CAS_ServiceBaseUrl_Interface
    +{
    +
    +    /**
    +     * Get PHP HTTP protocol and server name.
    +     *
    +     * @return string protocol, server hostname, and optionally port,
    +     *                without trailing slash (https://localhost:8443)
    +     */
    +    public function get();
    +
    +    /**
    +     * Check whether HTTPS is used.
    +     *
    +     * This is used to construct the protocol in the URL.
    +     *
    +     * @return bool true if HTTPS is used
    +     */
    +    public function isHttps();
    +
    +}
    
  • source/CAS/ServiceBaseUrl/Static.php+69 0 added
    @@ -0,0 +1,69 @@
    +<?php
    +
    +/**
    + * Licensed to Jasig under one or more contributor license
    + * agreements. See the NOTICE file distributed with this work for
    + * additional information regarding copyright ownership.
    + *
    + * Jasig licenses this file to you under the Apache License,
    + * Version 2.0 (the "License"); you may not use this file except in
    + * compliance with the License. You may obtain a copy of the License at:
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + *
    + * PHP Version 7
    + *
    + * @file     CAS/ServiceBaseUrl/Static.php
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +
    +/**
    + * Class that gets the server name of the PHP server by statically set
    + * hostname and port. This is used to generate service URL and PGT
    + * callback URL.
    + *
    + * @class    CAS_ServiceBaseUrl_Static
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +class CAS_ServiceBaseUrl_Static
    +extends CAS_ServiceBaseUrl_Base
    +{
    +    private $_name = null;
    +
    +    public function __construct($name) {
    +        if (is_string($name)) {
    +            $this->_name = $this->removeStandardPort($name);
    +        } else {
    +            throw new CAS_TypeMismatchException($name, '$name', 'string');
    +        }
    +    }
    +
    +    /**
    +     * Get the server name through static config.
    +     *
    +     * @return string the server hostname and port of the server configured
    +     */
    +    public function get()
    +    {
    +        phpCAS::traceBegin();
    +        phpCAS::trace("Returning static server name: " . $this->_name);
    +        phpCAS::traceEnd(true);
    +        return $this->_name;
    +    }
    +}
    \ No newline at end of file
    
  • test/CAS/Tests/AuthenticationTest.php+1 0 modified
    @@ -76,6 +76,7 @@ protected function setUp(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.clientapp.com', // Service Name
                 false // Start Session
             );
     
    
  • test/CAS/Tests/CallbackTest.php+1 0 modified
    @@ -67,6 +67,7 @@ public static function setUpBeforeClass(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.clientapp.com', // Service Name
                 false // Start Session
             );
     
    
  • test/CAS/Tests/Cas20AttributesTest.php+1 0 modified
    @@ -71,6 +71,7 @@ protected function setUp(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.service.com', // Service Name
                 false // Start Session
             );
     
    
  • test/CAS/Tests/LogTest.php+2 0 modified
    @@ -46,6 +46,7 @@ public function testSetLogger()
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.service.com', // Service Name
                 false // Start Session
             );
             $contents = file_get_contents($this->logPath);
    @@ -69,6 +70,7 @@ public function testSetLoggerNull()
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.service.com', // Service Name
                 false // Start Session
             );
             $contents = file_get_contents($this->logPath);
    
  • test/CAS/Tests/ProxyTicketValidationTest.php+1 0 modified
    @@ -73,6 +73,7 @@ protected function setUp(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.service.com', // Service Name
                 false // Start Session
             );
     
    
  • test/CAS/Tests/ServiceBaseUrlTest.php+244 0 added
    @@ -0,0 +1,244 @@
    +<?php
    +
    +/**
    + * Licensed to Jasig under one or more contributor license
    + * agreements. See the NOTICE file distributed with this work for
    + * additional information regarding copyright ownership.
    + *
    + * Jasig licenses this file to you under the Apache License,
    + * Version 2.0 (the "License"); you may not use this file except in
    + * compliance with the License. You may obtain a copy of the License at:
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + *
    + * PHP Version 7
    + *
    + * @file     CAS/Tests/ServiceBaseUrlTest.php
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +
    +namespace PhpCas\Tests;
    +
    +use PHPUnit\Framework\TestCase;
    +
    +/**
    + * Test class for verifying the operation of the ServiceBaseUrl classes.
    + *
    + * @class    ServiceBaseUrlTest
    + * @category Authentication
    + * @package  PhpCAS
    + * @author   Henry Pan <git@phy25.com>
    + * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
    + * @link     https://wiki.jasig.org/display/CASC/phpCAS
    + */
    +class ServiceBaseUrlTest extends TestCase
    +{
    +    /**
    +     * @var CAS_Client
    +     */
    +    protected $object;
    +
    +    const DEFAULT_NAME = 'https://default.domain';
    +
    +    const DOMAIN_1 = 'http://domain1';
    +
    +    /**
    +     * Sets up the fixture, for example, opens a network connection.
    +     * This method is called before a test is executed.
    +     */
    +    protected function setUp(): void
    +    {
    +        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array(self::DEFAULT_NAME, self::DOMAIN_1));
    +    }
    +
    +    /**
    +     * Tears down the fixture, for example, closes a network connection.
    +     * This method is called after a test is executed.
    +     */
    +    protected function tearDown(): void
    +    {
    +
    +    }
    +
    +    /*********************************************************
    +     * Tests of public (interface) methods
    +     *********************************************************/
    +
    +    /**
    +     * Verify that non allowlisted SERVER_NAME will return default name.
    +     *
    +     * @return void
    +     */
    +    public function testNonAllowlistedServerName()
    +    {
    +        $_SERVER['SERVER_NAME'] = 'domain1:8080';
    +
    +        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that non allowlisted HTTP_HOST will return default name.
    +     *
    +     * @return void
    +     */
    +    public function testNonAllowlistedHttpHost()
    +    {
    +        $_SERVER['HTTP_HOST'] = 'domain1:8080';
    +        $_SERVER['SERVER_NAME'] = '';
    +
    +        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that non allowlisted HTTP_X_FORWARDED_SERVER will return default name.
    +     *
    +     * @return void
    +     */
    +    public function testNonAllowlistedXForwardedServer()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_SERVER'] = 'domain1';
    +        $_SERVER['HTTP_X_FORWARDED_PORT'] = '8080';
    +
    +        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that non allowlisted HTTP_X_FORWARDED_SERVER will return.
    +     *
    +     * @return void
    +     */
    +    public function testNonAllowlistedXForwardedHost()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'domain1:8080';
    +
    +        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted SERVER_NAME will return in standarized form.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedServerName()
    +    {
    +        $_SERVER['SERVER_NAME'] = 'domain1';
    +        $_SERVER['HTTP_X_FORWARDED_PORT'] = '80';
    +
    +        $this->assertSame(self::DOMAIN_1, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted HTTP_HOST will return.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedHttpHost()
    +    {
    +        $_SERVER['HTTP_HOST'] = 'domain1';
    +        $_SERVER['SERVER_NAME'] = '';
    +        $_SERVER['HTTP_X_FORWARDED_PORT'] = '80';
    +
    +        $this->assertSame(self::DOMAIN_1, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted HTTP_X_FORWARDED_SERVER will return.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedXForwardedServer()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_SERVER'] = 'domain1';
    +        $_SERVER['HTTP_X_FORWARDED_PORT'] = '80';
    +
    +        $this->assertSame(self::DOMAIN_1, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedXForwardedHost()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'domain1';
    +
    +        $this->assertSame(self::DOMAIN_1, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return with a HTTP allowlist
    +     * that needs to be standardized.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedXForwardedHostHttpStandardized()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'domain1';
    +
    +        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array(self::DEFAULT_NAME, "http://domain1:80/"));
    +        $this->assertSame(self::DOMAIN_1, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return with a HTTP allowlist
    +     * that needs to be standardized.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedXForwardedHostWithSslHttpsStandardized()
    +    {
    +        $_SERVER['HTTPS'] = 'on';
    +        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'default.domain:443';
    +
    +        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array("https://default.domain:443/", self::DOMAIN_1));
    +        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return with a HTTP allowlist
    +     * that needs to be standardized.
    +     *
    +     * @return void
    +     */
    +    public function testAllowlistedXForwardedHostHttpsStandardized()
    +    {
    +        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'default.domain:443';
    +
    +        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array("https://default.domain:443/", "http://default.domain:443/"));
    +        $this->assertSame("http://default.domain:443", $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that static configuration always return the standardized base URL.
    +     *
    +     * @return void
    +     */
    +    public function testStaticHappyPath()
    +    {
    +        $this->object = new \CAS_ServiceBaseUrl_Static("https://default.domain:443/");
    +        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
    +    }
    +
    +    /**
    +     * Verify that static configuration always return the standardized base URL.
    +     *
    +     * @return void
    +     */
    +    public function testStaticNoProtocol()
    +    {
    +        $this->expectException(\CAS_InvalidArgumentException::class);
    +        $this->object = new \CAS_ServiceBaseUrl_Static("default.domain/");
    +    }
    +
    +}
    
  • test/CAS/Tests/ServiceMailTest.php+1 0 modified
    @@ -77,6 +77,7 @@ protected function setUp(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.clientapp.com', // Service Name
                 false // Start Session
             );
     
    
  • test/CAS/Tests/ServiceTicketValidationTest.php+1 0 modified
    @@ -73,6 +73,7 @@ protected function setUp(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.service.com', // Service Name
                 false // Start Session
             );
     
    
  • test/CAS/Tests/ServiceWebTest.php+1 0 modified
    @@ -75,6 +75,7 @@ protected function setUp(): void
                 'cas.example.edu', // Server Hostname
                 443, // Server port
                 '/cas/', // Server URI
    +            'http://www.clientapp.com', // Service Name
                 false // Start Session
             );
     
    
  • utils/version.properties+1 1 modified
    @@ -1,5 +1,5 @@
     # Version number devX, alphaX, RCX
    
    -phpcas.version=1.4.0
    
    +phpcas.version=1.6.0
    
     # devel, alpha, beta, stable
    
     phpcas.releaseStability=stable
    
     # devel, alpha, beta, stable
    
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.