How can I securely detect SSL in CakePHP behind an

2019-04-24 02:08发布

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.

3条回答
Melony?
2楼-- · 2019-04-24 02:30

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:

//AppController::beforeFilter
public function beforeFilter() {
    $this->request->addDetector('ssl', array(
        'env' => 'HTTP_X_FORWARDED_PROTO',
        'value' => 'https'
    ));
}

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.:

$ curl --header "X-Forwarded-Proto:https" http:://yoursite.com

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.

查看更多
Anthone
3楼-- · 2019-04-24 02:38

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:

<IfModule mod_rpaf.c>
    RPAF_Enable       On
    RPAF_ProxyIPs     127.0.0.1 10.0.0.0/24
    RPAF_SetHostName  On
    RPAF_SetHTTPS     On
    RPAF_SetPort      On
</IfModule>

# If mod_rewrite redirects then we lose the HTTPS status to REDIRECT_HTTPS.
# This resets it back. This happens with Cake's front controller
<IfModule setenvif_module>
    SetEnvIf REDIRECT_HTTPS on HTTPS=on
</IfModule>
查看更多
爷的心禁止访问
4楼-- · 2019-04-24 02:38

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:

function redirect($url, $status = NULL, $exit = true) {
    return parent::redirect(str_replace('http://', 'https://', Router::url($url, true)));
}
查看更多
登录 后发表回答