riverside / php-express

:horse: PHP micro-framework inspired by Express.js
https://riverside.github.io/php-express/
MIT License
26 stars 10 forks source link

Robustify how Request->scheme and Request->secure are calculated #11

Closed jameswilson closed 4 months ago

jameswilson commented 4 months ago

Problem statement

Using $_SERVER['REQUEST_SCHEME'] === 'https' is not always trustworthy. First of all, it's only available in Apache >=2.4. It doesn't even exist in nginx, IIS, lighttpd and other web servers.

Proposed resolution

Create a protected method Request::getRequestScheme() that can properly calculate whether http or https was used. Use this function to set Request->scheme and Request->secure member variables.

   protected static function getRequestScheme() : bool
    {
        // Modern servers will have the HTTPS header set to 'on' or '1'.
        if (
            isset($_SERVER['HTTPS'])
            && (
                strtolower($_SERVER['HTTPS']) == 'on'
                || $_SERVER['HTTPS'] == 1
            )
        ) {
            return 'https';
        }
        // Some reverse proxies and load balencers will have the
        // HTTP_X_FORWARDED_PROTO header set to 'https'.
        else if (
            isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
            && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
        ) {

            return 'https';
        }
        // Other reverse proxies and load balencers will have the
        // HTTP_FRONT_END_HTTPS headers set to 'on' or '1'.
        else if (
            isset($_SERVER['HTTP_FRONT_END_HTTPS'])
            && (
                strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) == 'on'
                || $_SERVER['HTTP_FRONT_END_HTTPS'] == 1
            )
        ) {
            return 'https';
        }
        // Apache server may have the REQUEST_SCHEME header available.
        else if (
            isset($_SERVER['REQUEST_SCHEME'])
            && strtolower($_SERVER['REQUEST_SCHEME']) == 'https'
        ) {
            return 'https';
        }
        // If all else fails, try the standard SSL server port '443'.
        else if (
            isset($_SERVER['SERVER_PORT'])
            && intval($_SERVER['SERVER_PORT']) === 443
        ) {
            return 'https';
        }
        return 'http';
    }