CakePHP (all versions that I've seen) check against $_SERVER['HTTPS']
to see whether a request has been made over HTTPS instead of plain HTTP.
I'm using nginx as a load balancer, behind which are the Apache application servers. Since the SSL connection terminates at the load balancer, $_SERVER['HTTPS']
is not set as far as CakePHP is concerned.
I'd like to find a secure way to detect HTTPS on the app servers.
So far, I've put this into my CakePHP configuration:
$request_headers = getallheaders();
if ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) || ( isset($request_headers['X-Forwarded-Proto']) && $request_headers['X-Forwarded-Proto'] == 'https' ) ) {
$ssl = true;
// overwrite environment vars (ugly) since CakePHP won't honour X-Forwarded-Proto
$_SERVER['HTTPS'] = 'on';
$_ENV['HTTPS'] = 'on';
} else {
$ssl = false;
}
And then in the nginx configuration, I've used proxy_set_header X-Forwarded-Proto https;
to add the flag to any requests between the load balancer and the back-end application servers.
This works perfectly fine, but anyone making a direct request to the app servers could fool them into thinking they are browsing over SSL when they're not. I'm not sure whether this is a security risk, but it doesn't seem like a good idea.
Is it a security risk? What's the better solution?
Since using X-Forwarded-Proto
seems like something of a standard, the solution may be a good patch to be submitted to the CakePHP core, so I think any answer can legitimately involve editing core files too.
Add a request detector
Do not edit the core. If your intention is to submit a patch, don't rely on your patch until after it's accepted - Otherwise you're on a road to divergence and maintaining your own fork of CakePHP.
Once you determine your exact implementation logic you can use a request detector to honor it.
for example:
After this, your custom header will correctly be identified by cake as an ssl request.
Note that the keys of the
$_SERVER
global are normalized as all caps and underscore delimited i.e.:Will populate
$_SERVER['HTTP_X_FORWARDED_PROTO']
- as such this is the key to check for.Account for (in)security
Yes, it's something you should take care of - either disable direct access to your webservers so that only via the ip of the loadbalancer will it respond at all; or modify your detector so that it does not return true for direct-access requests irrespective of the value of the
X-Forwarded-Proto
header - as shown in the documentation you can use a callback to perform whatever logic required rather than simply testing the values of some environment variable.mod_rpaf will let you do this.
This sets the HTTPS value in Apache to "on" based on the headers sent by nginx so Cake will work out of the box (as well as any other apps run in Apache).
It also corrects the values for REMOTE_ADDR, SERVER_PORT and HTTP_HOST.
Here is my example config:
I found the comment of AD7six - override the redirect() function - extremely helpful in my SSL-only setup since my app had some redirects going to http.
I just added the following to
AppController.php
so the redirect function always uses https: