Felix Eckhofer
2014-03-26 f58a294949547ed132bf3cdb5815b68c659b992a
Add config variable 'proxy_whitelist'

HTTP headers X_FORWARDED_* and X_REAL_IP are only evaluated when
received from an IP listed in proxy_whitelist. Furthermore, only the
last non-trusted IP from X-Forwarded-For is used in place of the real
ip.

Without this, an attacker can easily spoof the headers and control the
result of the ip or ssl check.

This fixes several problems with [3a4c9f42], [4d480b36] and [a520f331] as
mentioned in #1489729.

Conflicts:
CHANGELOG
3 files modified
30 ■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
config/defaults.inc.php 4 ●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_utils.php 25 ●●●● patch | view | raw | blame | history
CHANGELOG
@@ -15,6 +15,7 @@
- Fix missing Mail-Followup-To header in sent mail (#1489829)
- Fix error when spell-checking an empty text (#1489831)
- Avoid popupmenus being closed when scrollbar is clicked (#1489832)
- Add proxy_whitelist configuration option (#1489729)
RELEASE 1.0.0
-------------
config/defaults.inc.php
@@ -358,6 +358,10 @@
// check client IP in session athorization
$config['ip_check'] = false;
// List of trusted proxies
// X_FORWARDED_* and X_REAL_IP headers are only accepted from these IPs
$config['proxy_whitelist'] = array();
// check referer of incoming requests
$config['referer_check'] = false;
program/lib/Roundcube/rcube_utils.php
@@ -596,7 +596,9 @@
        if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') {
            return true;
        }
        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') {
        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])
            && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
            && in_array($_SERVER['REMOTE_ADDR'], rcube::get_instance()->config->get('proxy_whitelist', array()))) {
            return true;
        }
        if ($port && $_SERVER['SERVER_PORT'] == $port) {
@@ -681,13 +683,22 @@
     */
    public static function remote_addr()
    {
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 2);
            return $hosts[0];
        }
        // Check if any of the headers are set first to improve performance
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) || !empty($_SERVER['HTTP_X_REAL_IP'])) {
            $proxy_whitelist = rcube::get_instance()->config->get('proxy_whitelist', array());
            if (in_array($_SERVER['REMOTE_ADDR'], $proxy_whitelist)) {
                if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                    foreach(array_reverse(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])) as $forwarded_ip) {
                        if (!in_array($forwarded_ip, $proxy_whitelist)) {
                            return $forwarded_ip;
                        }
                    }
                }
        if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
            return $_SERVER['HTTP_X_REAL_IP'];
                if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
                    return $_SERVER['HTTP_X_REAL_IP'];
                }
            }
        }
        if (!empty($_SERVER['REMOTE_ADDR'])) {