<?php
/**
* GET an HTTP URL to retrieve its content
- * Uses the cURL library or a fallback method
+ * Uses the cURL library or a fallback method
*
* @param string $url URL to get (http://...)
* @param int $timeout network timeout (in seconds)
return array_pop($ips);
}
+
+/**
+ * Return an identifier based on the advertised client IP address(es)
+ *
+ * This aims at preventing session hijacking from users behind the same proxy
+ * by relying on HTTP headers.
+ *
+ * See:
+ * - https://secure.php.net/manual/en/reserved.variables.server.php
+ * - https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php
+ * - https://stackoverflow.com/questions/12233406/preventing-session-hijacking
+ * - https://stackoverflow.com/questions/21354859/trusting-x-forwarded-for-to-identify-a-visitor
+ *
+ * @param array $server The $_SERVER array
+ *
+ * @return string An identifier based on client IP address information
+ */
+function client_ip_id($server)
+{
+ $ip = $server['REMOTE_ADDR'];
+
+ if (isset($server['HTTP_X_FORWARDED_FOR'])) {
+ $ip = $ip . '_' . $server['HTTP_X_FORWARDED_FOR'];
+ }
+ if (isset($server['HTTP_CLIENT_IP'])) {
+ $ip = $ip . '_' . $server['HTTP_CLIENT_IP'];
+ }
+ return $ip;
+}
+
+
/**
* Returns true if Shaarli's currently browsed in HTTPS.
* Supports reverse proxies (if the headers are correctly set).
}
// If session does not exist on server side, or IP address has changed, or session has expired, logout.
if (empty($_SESSION['uid'])
- || ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != allIPs())
+ || ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != client_ip_id($_SERVER))
|| time() >= $_SESSION['expires_on'])
{
logout();
// ------------------------------------------------------------------------------------------
// Session management
-// Returns the IP address of the client (Used to prevent session cookie hijacking.)
-function allIPs()
-{
- $ip = $_SERVER['REMOTE_ADDR'];
- // Then we use more HTTP headers to prevent session hijacking from users behind the same proxy.
- if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip=$ip.'_'.$_SERVER['HTTP_X_FORWARDED_FOR']; }
- if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip=$ip.'_'.$_SERVER['HTTP_CLIENT_IP']; }
- return $ip;
-}
-
/**
* Load user session.
*
function fillSessionInfo($conf)
{
$_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid)
- $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked.
+ $_SESSION['ip'] = client_ip_id($_SERVER);
$_SESSION['username']= $conf->get('credentials.login');
$_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration.
}
--- /dev/null
+<?php
+/**
+ * HttpUtils' tests
+ */
+
+require_once 'application/HttpUtils.php';
+
+/**
+ * Unitary tests for client_ip_id()
+ */
+class ClientIpIdTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Get a remote client ID based on its IP
+ */
+ public function testClientIpIdRemote()
+ {
+ $this->assertEquals(
+ '10.1.167.42',
+ client_ip_id(['REMOTE_ADDR' => '10.1.167.42'])
+ );
+ }
+
+ /**
+ * Get a remote client ID based on its IP and proxy information (1)
+ */
+ public function testClientIpIdRemoteForwarded()
+ {
+ $this->assertEquals(
+ '10.1.167.42_127.0.1.47',
+ client_ip_id([
+ 'REMOTE_ADDR' => '10.1.167.42',
+ 'HTTP_X_FORWARDED_FOR' => '127.0.1.47'
+ ])
+ );
+ }
+
+ /**
+ * Get a remote client ID based on its IP and proxy information (2)
+ */
+ public function testClientIpIdRemoteForwardedClient()
+ {
+ $this->assertEquals(
+ '10.1.167.42_10.1.167.56_127.0.1.47',
+ client_ip_id([
+ 'REMOTE_ADDR' => '10.1.167.42',
+ 'HTTP_X_FORWARDED_FOR' => '10.1.167.56',
+ 'HTTP_CLIENT_IP' => '127.0.1.47'
+ ])
+ );
+ }
+}