diff --git a/bootstrap.php b/bootstrap.php
index e70c79a6bb9801b06c49e4aaa5349a93e84b6de6..fe3dd2155b8fdd0e2dbb21d249d5eeba247fadc0 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -50,11 +50,7 @@ class Bootstrap {
     }
 
     function https() {
-       return
-            (isset($_SERVER['HTTPS'])
-                && strtolower($_SERVER['HTTPS']) == 'on')
-            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
-                && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https');
+       return osTicket::is_https();
     }
 
     static function defineTables($prefix) {
@@ -335,6 +331,7 @@ ini_set('include_path', './'.PATH_SEPARATOR.INCLUDE_DIR.PATH_SEPARATOR.PEAR_DIR)
 require(INCLUDE_DIR.'class.osticket.php');
 require(INCLUDE_DIR.'class.misc.php');
 require(INCLUDE_DIR.'class.http.php');
+require(INCLUDE_DIR.'class.validator.php');
 
 // Determine the path in the URI used as the base of the osTicket
 // installation
@@ -346,12 +343,7 @@ Bootstrap::init();
 #CURRENT EXECUTING SCRIPT.
 define('THISPAGE', Misc::currentURL());
 
-define('DEFAULT_MAX_FILE_UPLOADS',ini_get('max_file_uploads')?ini_get('max_file_uploads'):5);
-define('DEFAULT_PRIORITY_ID',1);
+define('DEFAULT_MAX_FILE_UPLOADS', ini_get('max_file_uploads') ?: 5);
+define('DEFAULT_PRIORITY_ID', 1);
 
-#Global override
-if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
-    // Take the left-most item for X-Forwarded-For
-    $_SERVER['REMOTE_ADDR'] = trim(array_pop(
-        explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])));
 ?>
diff --git a/include/class.osticket.php b/include/class.osticket.php
index 40c939c0493a30d685ea7c51fac0d808eb5b39a5..db48388e03dc852c9a9ddc06ffe9c9bf7a030211 100644
--- a/include/class.osticket.php
+++ b/include/class.osticket.php
@@ -444,6 +444,37 @@ class osTicket {
         }
     }
 
+   /*
+    * getTrustedProxies
+    *
+    * Get defined trusted proxies
+    */
+
+    static function getTrustedProxies() {
+        static $proxies = null;
+        // Parse trusted proxies from config file
+        if (!isset($proxies) && defined('TRUSTED_PROXIES'))
+            $proxies = array_filter(
+                    array_map('trim', explode(',', TRUSTED_PROXIES)));
+
+        return $proxies ?: array();
+    }
+
+    /*
+     * getLocalNetworkAddresses
+     *
+     * Get defined local network addresses
+     */
+    static function getLocalNetworkAddresses() {
+        static $ips = null;
+        // Parse local addreses from config file
+        if (!isset($ips) && defined('LOCAL_NETWORKS'))
+            $ips = array_filter(
+                    array_map('trim', explode(',', LOCAL_NETWORKS)));
+
+        return $ips ?: array();
+    }
+
     static function get_root_path($dir) {
 
         /* If run from the commandline, DOCUMENT_ROOT will not be set. It is
@@ -488,14 +519,96 @@ class osTicket {
         return null;
     }
 
+    /*
+     * get_client_ip
+     *
+     * Get client IP address from "Http_X-Forwarded-For" header by following a
+     * chain of trusted proxies.
+     *
+     * "Http_X-Forwarded-For" header value is a comma+space separated list of IP
+     * addresses, the left-most being the original client, and each successive
+     * proxy that passed the request all the way to the originating IP address.
+     *
+     */
+    static function get_client_ip($header='HTTP_X_FORWARDED_FOR') {
+
+        // Request IP
+        $ip = $_SERVER['REMOTE_ADDR'];
+        // Trusted proxies.
+        $proxies = self::getTrustedProxies();
+        // Return current IP address if header is not set and
+        // request is not from a trusted proxy.
+        if (!isset($_SERVER[$header])
+                || !$proxies
+                || !self::is_trusted_proxy($ip, $proxies))
+            return $ip;
+
+        // Get chain of proxied ip addresses
+        $ips = array_map('trim', explode(',', $_SERVER[$header]));
+        // Add request IP to the chain
+        $ips[] = $ip;
+        // Walk the chain in reverse - remove invalid IPs
+        $ips = array_reverse($ips);
+        foreach ($ips as $k => $ip) {
+            // Make sure the IP is valid and not a trusted proxy
+            if ($k && !Validator::is_ip($ip))
+                unset($ips[$k]);
+            elseif ($k && !self::is_trusted_proxy($ip, $proxies))
+                return $ip;
+        }
+
+        // We trust the 400 lb hacker... return left most valid IP
+        return array_pop($ips);
+    }
+
+    /*
+     * Checks if the IP is that of a trusted proxy
+     *
+     */
+    static function is_trusted_proxy($ip, $proxies=array()) {
+        $proxies = $proxies ?: self::getTrustedProxies();
+        // We don't have any proxies set.
+        if (!$proxies)
+            return false;
+        // Wildcard set - trust all proxies
+        else if ($proxies == '*')
+            return true;
+
+        return ($proxies && Validator::check_ip($ip, $proxies));
+    }
+
+    /**
+     * is_local_ip
+     *
+     * Check if a given IP is part of defined local address blocks
+     *
+     */
+    static function is_local_ip($ip, $ips=array()) {
+        $ips = $ips
+            ?: self::getLocalNetworkAddresses()
+            ?: array();
+
+        foreach ($ips as $addr) {
+            if (Validator::check_ip($ip, $addr))
+                return true;
+        }
+
+        return false;
+    }
+
     /**
      * Returns TRUE if the request was made via HTTPS and false otherwise
      */
     function is_https() {
-        return (isset($_SERVER['HTTPS'])
+
+        // Local server flags
+        if (isset($_SERVER['HTTPS'])
                 && strtolower($_SERVER['HTTPS']) == 'on')
-            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
-                && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https');
+            return true;
+
+        // Check if SSL was terminated by a loadbalancer
+        return (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
+                && !strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https'));
     }
 
     /* returns true if script is being executed via commandline */
diff --git a/include/class.validator.php b/include/class.validator.php
index 2cce38f21432dc457efbae59bd4277bec9d213d3..14be7ccaf005423bf34c395aa375c04227e9671e 100644
--- a/include/class.validator.php
+++ b/include/class.validator.php
@@ -185,23 +185,7 @@ class Validator {
     }
 
     static function is_ip($ip) {
-
-        if(!$ip or empty($ip))
-            return false;
-
-        $ip=trim($ip);
-        # Thanks to http://stackoverflow.com/a/1934546
-        if (function_exists('inet_pton')) { # PHP 5.1.0
-            # Let the built-in library parse the IP address
-            return @inet_pton($ip) !== false;
-        } else if (preg_match(
-            '/^(?>(?>([a-f0-9]{1,4})(?>:(?1)){7}|(?!(?:.*[a-f0-9](?>:|$)){7,})'
-            .'((?1)(?>:(?1)){0,5})?::(?2)?)|(?>(?>(?1)(?>:(?1)){5}:|(?!(?:.*[a-f0-9]:){5,})'
-            .'(?3)?::(?>((?1)(?>:(?1)){0,3}):)?)?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])'
-            .'(?>\.(?4)){3}))$/iD', $ip)) {
-            return true;
-        }
-        return false;
+        return filter_var(trim($ip), FILTER_VALIDATE_IP) !== false;
     }
 
     static function is_username($username, &$error='') {
@@ -212,6 +196,100 @@ class Validator {
         return $error == '';
     }
 
+
+    /*
+     * check_ip
+     * Checks if an IP (IPv4 or IPv6) address is contained in the list of given IPs or subnets.
+     *
+     * @credit - borrowed from Symfony project
+     *
+     */
+    public static function check_ip($ip, $ips) {
+
+        if (!Validator::is_ip($ip))
+            return false;
+
+        $method = substr_count($ip, ':') > 1 ? 'check_ipv6' : 'check_ipv4';
+        $ips = is_array($ips) ? $ips : array($ips);
+        foreach ($ips as $_ip) {
+            if (self::$method($ip, $_ip)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * check_ipv4
+     * Compares two IPv4 addresses.
+     * In case a subnet is given, it checks if it contains the request IP.
+     *
+     * @credit - borrowed from Symfony project
+     */
+    public static function check_ipv4($ip, $cidr) {
+
+        if (false !== strpos($cidr, '/')) {
+            list($address, $netmask) = explode('/', $cidr, 2);
+
+            if ($netmask === '0')
+                return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+
+            if ($netmask < 0 || $netmask > 32)
+                return false;
+
+        } else {
+            $address = $cidr;
+            $netmask = 32;
+        }
+
+        return 0 === substr_compare(
+                sprintf('%032b', ip2long($ip)),
+                sprintf('%032b', ip2long($address)),
+                0, $netmask);
+    }
+
+    /**
+     * Compares two IPv6 addresses.
+     * In case a subnet is given, it checks if it contains the request IP.
+     *
+     * @credit - borrowed from Symfony project
+     * @author David Soria Parra <dsp at php dot net>
+     *
+     * @see https://github.com/dsp/v6tools
+     *
+     */
+    public static function check_ipv6($ip, $cidr) {
+
+        if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1')))
+            return false;
+
+        if (false !== strpos($cidr, '/')) {
+            list($address, $netmask) = explode('/', $cidr, 2);
+            if ($netmask < 1 || $netmask > 128)
+                return false;
+        } else {
+            $address = $cidr;
+            $netmask = 128;
+        }
+
+        $bytesAddr = unpack('n*', @inet_pton($address));
+        $bytesTest = unpack('n*', @inet_pton($ip));
+        if (!$bytesAddr || !$bytesTest)
+            return false;
+
+        for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
+            $left = $netmask - 16 * ($i - 1);
+            $left = ($left <= 16) ? $left : 16;
+            $mask = ~(0xffff >> $left) & 0xffff;
+            if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     function process($fields,$vars,&$errors){
 
         $val = new Validator();
diff --git a/include/ost-sampleconfig.php b/include/ost-sampleconfig.php
index 0b26400698271c1d89ab46f55a209d501b5b1dae..b4a5049f4d01b5a9596c0142211f41bd19e73cbd 100644
--- a/include/ost-sampleconfig.php
+++ b/include/ost-sampleconfig.php
@@ -107,6 +107,39 @@ define('TABLE_PREFIX','%CONFIG-PREFIX');
 
 # define('ROOT_PATH', '/support/');
 
+
+# Option: TRUSTED_PROXIES (default: <none>)
+#
+# To support running osTicket installation on a web servers that sit behind a
+# load balancer, HTTP cache, or other intermediary (reverse) proxy; it's
+# necessary to define trusted proxies to protect against forged http headers
+#
+# osTicket supports passing the following http headers from a trusted proxy;
+# - HTTP_X_FORWARDED_FOR    =>  Chain of client's IPs
+# - HTTP_X_FORWARDED_PROTO  =>  Client's HTTP protocal (http | https)
+#
+# You'll have to explicitly define comma separated IP addreseses or CIDR of
+# upstream proxies to trust. Wildcard "*" (not recommended) can be used to
+# trust all chained IPs as proxies in cases that ISP/host doesn't provide
+# IPs of loadbalancers or proxies.
+#
+# References:
+# http://en.wikipedia.org/wiki/X-Forwarded-For
+#
+
+define('TRUSTED_PROXIES', '');
+
+
+# Option: LOCAL_NETWORKS (default: 127.0.0.0/24)
+#
+# When running osTicket as part of a cluster it might become necessary to
+# whitelist local/virtual networks that can bypass some authentication/checks.
+#
+# define comma separated IP addreseses or enter CIDR of local network.
+
+define('LOCAL_NETWORKS', '127.0.0.0/24');
+
+
 #
 # Session Storage Options
 # ---------------------------------------------------
diff --git a/include/staff/syslogs.inc.php b/include/staff/syslogs.inc.php
index 0d3f05e216a6e898f93b2a1134f497a2746ee6cf..9d4d43df1dbd020a61be1defc16109591bff1db3 100644
--- a/include/staff/syslogs.inc.php
+++ b/include/staff/syslogs.inc.php
@@ -156,7 +156,7 @@ else
                 <td>&nbsp;<a class="tip" href="#log/<?php echo $row['log_id']; ?>"><?php echo Format::htmlchars($row['title']); ?></a></td>
                 <td><?php echo $row['log_type']; ?></td>
                 <td>&nbsp;<?php echo Format::daydatetime($row['created']); ?></td>
-                <td><?php echo $row['ip_address']; ?></td>
+                <td><?php echo Format::htmlchars($row['ip_address']); ?></td>
             </tr>
             <?php
             } //end of while.
diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php
index 0ebf5cbf2e134d6d557556c053b848da35e516c6..cc93efcc3f97091ecaeafe51e3679ce1ea252e8c 100644
--- a/include/staff/ticket-view.inc.php
+++ b/include/staff/ticket-view.inc.php
@@ -355,7 +355,7 @@ if($ticket->isOverdue())
                         echo Format::htmlchars($ticket->getSource());
 
                         if (!strcasecmp($ticket->getSource(), 'Web') && $ticket->getIP())
-                            echo '&nbsp;&nbsp; <span class="faded">('.$ticket->getIP().')</span>';
+                            echo '&nbsp;&nbsp; <span class="faded">('.Format::htmlchars($ticket->getIP()).')</span>';
                         ?>
                     </td>
                 </tr>
diff --git a/main.inc.php b/main.inc.php
index 026e440ca84cc904d8a5ebcf8e6b5101cd81d41e..e92ec4a71d3e59876361fba0aa8446328b383779 100644
--- a/main.inc.php
+++ b/main.inc.php
@@ -27,6 +27,9 @@ Bootstrap::i18n_prep();
 Bootstrap::loadCode();
 Bootstrap::connect();
 
+#Global override
+$_SERVER['REMOTE_ADDR'] = osTicket::get_client_ip();
+
 if(!($ost=osTicket::start()) || !($cfg = $ost->getConfig()))
 Bootstrap::croak(__('Unable to load config info from DB. Get tech support.'));
 
diff --git a/setup/test/tests/test.validation.php b/setup/test/tests/test.validation.php
index bce9fe85505d08e41251fc127e230bba9f775394..0297d51d7d61535f82e27548af169aa7222bb46d 100644
--- a/setup/test/tests/test.validation.php
+++ b/setup/test/tests/test.validation.php
@@ -54,6 +54,19 @@ class TestValidation extends Test {
         #$this->assert(Validator::is_email('δοκιμή@παράδειγμα.δοκιμή'));
         #$this->assert(Validator::is_email('甲斐@黒川.日本'));
     }
+
+    function testIPAddresses() {
+
+        // Validate IP Addreses
+        $this->assert(Validator::is_ip('127.0.0.1'));
+        $this->assert(Validator::is_ip('192.168.129.74'));
+
+        // Test IP check
+        $this->assert(Validator::check_ip('127.0.0.1', '127.0.0.0/24'));
+        $this->assert(Validator::check_ip('192.168.129.42',
+                    ['127.0.0.0/24', '192.168.129.0/24']));
+        $this->assert(!Validator::check_ip('10.0.5.15', '127.0.0.0/24'));
+    }
 }
 return 'TestValidation';
 ?>