Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request HTTPS Detection #1861

Closed
Demonslay335 opened this issue May 7, 2016 · 7 comments
Closed

Request HTTPS Detection #1861

Demonslay335 opened this issue May 7, 2016 · 7 comments

Comments

@Demonslay335
Copy link

Demonslay335 commented May 7, 2016

This caused a bit of headache for me when using HMAC authentication on the request URI. The Request object does not return the proper protocol on proxied SSL services such as CloudFlare, where the regular $_SERVER['HTTPS'] is not present.

<?php

// New app instance
$app = new \Slim\App;

// Middleware closure
$app->add(function(ServerRequestInterface $request, ResponseInterface $response, callable $next){

    // Get request URI
    $uri = $request->getUri();

});
?>

On a CloudFlare host (and probably other proxy hosts) accessed via https, $uri is an incorrect scheme ("http://example.com"). It would be expected for $uri to be "https://example.com".

I have tracked this down to the following line at \Slim\Http\Uri::createFromEnvironment().

...
public static function createFromEnvironment(Environment $env)
    {
        // Scheme
        $isSecure = $env->get('HTTPS');
        $scheme = (empty($isSecure) || $isSecure === 'off') ? 'http' : 'https';
...

I believe a fallback should be implemented to account for proxied services that actually deliver this information in $_SERVER['HTTP_X_FORWARDED_PROTO'] ("https" or "http").

I can certainly create a PR for this, but wasn't sure if since this is relating to a PSR7 compliant object, if the rabbit hole goes deeper than just this project.

*Edit
Did a bit of digging on StackOverflow, and it seems some Microsoft products may use Front-End-Https: on, and some reverse proxies may be X-Url-Scheme: https.

@tuupola
Copy link
Contributor

tuupola commented May 7, 2016

IMO current behaviour is correct. Slim instance itself is accessed via http and the scheme is set to http. If you want to override the scheme via user settable header it can be done with middleware.

$app->add(function($request, $response, $next) {
    $scheme = $request->getHeader("X-Url-Scheme")[0];
    $uri = $request->getUri()->withScheme($scheme);
    $request = $request->withUri($uri);
    return $next($request, $response);
});

However one should note that user settable headers are not really trustworthy.

@Demonslay335
Copy link
Author

I don't quite understand what you mean by the Slim instance being accessed by http. If I run this code on my local Apache setup (e.g. http://localhost) getUri() returns https as I would expect.

The host is determined by two different environment keys in the next logic block, so I would propose a similar chained approach to determining the scheme.

Adding more middleware is a bit overkill in my instance TBH (just used to make an HMAC hash, so I hacked a str_replace() on mine since SSL is forced anyways via server config), but thanks for the code. Definitely would come in handy if I needed it in a bigger project.

@tuupola
Copy link
Contributor

tuupola commented May 7, 2016

Browser makes request to Cloudflare proxy over https. Cloudflare proxy makes request to Slim instance over http. Thus Slim instance is accessed over http. This is if you are using their Flexible SSL offering.

https://www.cloudflare.com/ssl/

If you are using their Full SSL or Full SSL strict the traffic between Cloudflare proxy and your webserver will be over https and the problem you described most likely does not exist.

@akrabat
Copy link
Member

akrabat commented May 7, 2016

This is exactly the kind of thing that Middleware is for. X-Forwarded-Proto is not trustworthy, so you should also check that the REMOTR_ADDR is the correct IP address. This is more work than should be in core.

One such middleware is rka-scheme-and-host-detection-middleware.

@akrabat akrabat closed this as completed May 7, 2016
@Demonslay335
Copy link
Author

@tuupola Thanks for the explanation. Excuse my ignorance with how the SSL proxy part works, my hosting is off of someone else's sub-domain, so I've never actually managed CF myself.

@akrabat I agree it would be too much work for the core now that I have re-evaluated with the comments here. Thanks for the link, surprised I never came across that middleware while looking into this, very useful! I might just pull that into the project to make things nicer than my aforementioned str_replace().

@madsem
Copy link

madsem commented Jul 20, 2018

I just ran into the same issue, hosting behind an ELB on Amazon.
X-Forwarded-Proto is trustworthy as long as the instances are not accessible via public and security groups allow access only via load balancer.

The problem with the middleware solution I see is that middleware runs much later so that until this point the application still generates http urls.

IMHO it should be possible to add trusted headers directly to the environment, because this is not an edge case but a very common use case.

PS: Just tried it with Middleware, but the problem remains that Slim\Http\Uri generates http urls, also when

PSPS:

I ended up writing a service provider (because I use php league container) and create the environment like this:

$this->container->share('environment', function () {

            $server = $_SERVER;

            // fix the secure environment detection if behind an AWS ELB
            if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
                $server['HTTPS'] = 'on';
                $server['SERVER_PROTOCOL'] = 'HTTP/2.0';
                $server['REQUEST_SCHEME'] = 'https';
            }

            return new Environment($server);
        });

Now for my application this is sufficient, because it is not accessible publicly and only though the aws elb, so I know those headers are 100% from the elb. Now the request object builds using the environment data and all urls taken from the request also have https even though slim runs "officially" under http on the instance.

@Radiergummi
Copy link

Same issue for me - I'm running php-fpm behind nginx, which is a fairly common setup these days, I'd say.
To anyone coming here via Google: Set the following fastCGI parameter in your nginx configuration.

fastcgi_param HTTPS 'on';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants