aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorVirtualTam <virtualtam@flibidi.net>2018-02-16 21:51:44 +0100
committerVirtualTam <virtualtam@flibidi.net>2018-05-29 22:53:54 +0200
commit88110550b89617dcda16441212599b8a40faa20c (patch)
treeac1f137c96ca4df448a802a339fd7a351ce16bcd
parent8f816d8ddfe9219e15580cef6e5c9037d1d4fd28 (diff)
downloadShaarli-88110550b89617dcda16441212599b8a40faa20c.tar.gz
Shaarli-88110550b89617dcda16441212599b8a40faa20c.tar.zst
Shaarli-88110550b89617dcda16441212599b8a40faa20c.zip
Refactor client session hijacking protection
Signed-off-by: VirtualTam <virtualtam@flibidi.net>
-rw-r--r--application/HttpUtils.php33
-rw-r--r--index.php14
-rw-r--r--tests/HttpUtils/ClientIpIdTest.php52
3 files changed, 86 insertions, 13 deletions
diff --git a/application/HttpUtils.php b/application/HttpUtils.php
index 83a4c5e2..e9282506 100644
--- a/application/HttpUtils.php
+++ b/application/HttpUtils.php
@@ -1,7 +1,7 @@
1<?php 1<?php
2/** 2/**
3 * GET an HTTP URL to retrieve its content 3 * GET an HTTP URL to retrieve its content
4 * Uses the cURL library or a fallback method 4 * Uses the cURL library or a fallback method
5 * 5 *
6 * @param string $url URL to get (http://...) 6 * @param string $url URL to get (http://...)
7 * @param int $timeout network timeout (in seconds) 7 * @param int $timeout network timeout (in seconds)
@@ -415,6 +415,37 @@ function getIpAddressFromProxy($server, $trustedIps)
415 return array_pop($ips); 415 return array_pop($ips);
416} 416}
417 417
418
419/**
420 * Return an identifier based on the advertised client IP address(es)
421 *
422 * This aims at preventing session hijacking from users behind the same proxy
423 * by relying on HTTP headers.
424 *
425 * See:
426 * - https://secure.php.net/manual/en/reserved.variables.server.php
427 * - https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php
428 * - https://stackoverflow.com/questions/12233406/preventing-session-hijacking
429 * - https://stackoverflow.com/questions/21354859/trusting-x-forwarded-for-to-identify-a-visitor
430 *
431 * @param array $server The $_SERVER array
432 *
433 * @return string An identifier based on client IP address information
434 */
435function client_ip_id($server)
436{
437 $ip = $server['REMOTE_ADDR'];
438
439 if (isset($server['HTTP_X_FORWARDED_FOR'])) {
440 $ip = $ip . '_' . $server['HTTP_X_FORWARDED_FOR'];
441 }
442 if (isset($server['HTTP_CLIENT_IP'])) {
443 $ip = $ip . '_' . $server['HTTP_CLIENT_IP'];
444 }
445 return $ip;
446}
447
448
418/** 449/**
419 * Returns true if Shaarli's currently browsed in HTTPS. 450 * Returns true if Shaarli's currently browsed in HTTPS.
420 * Supports reverse proxies (if the headers are correctly set). 451 * Supports reverse proxies (if the headers are correctly set).
diff --git a/index.php b/index.php
index 2fe3f821..08a69327 100644
--- a/index.php
+++ b/index.php
@@ -207,7 +207,7 @@ function setup_login_state($conf)
207 } 207 }
208 // If session does not exist on server side, or IP address has changed, or session has expired, logout. 208 // If session does not exist on server side, or IP address has changed, or session has expired, logout.
209 if (empty($_SESSION['uid']) 209 if (empty($_SESSION['uid'])
210 || ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != allIPs()) 210 || ($conf->get('security.session_protection_disabled') === false && $_SESSION['ip'] != client_ip_id($_SERVER))
211 || time() >= $_SESSION['expires_on']) 211 || time() >= $_SESSION['expires_on'])
212 { 212 {
213 logout(); 213 logout();
@@ -231,16 +231,6 @@ $userIsLoggedIn = setup_login_state($conf);
231// ------------------------------------------------------------------------------------------ 231// ------------------------------------------------------------------------------------------
232// Session management 232// Session management
233 233
234// Returns the IP address of the client (Used to prevent session cookie hijacking.)
235function allIPs()
236{
237 $ip = $_SERVER['REMOTE_ADDR'];
238 // Then we use more HTTP headers to prevent session hijacking from users behind the same proxy.
239 if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip=$ip.'_'.$_SERVER['HTTP_X_FORWARDED_FOR']; }
240 if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip=$ip.'_'.$_SERVER['HTTP_CLIENT_IP']; }
241 return $ip;
242}
243
244/** 234/**
245 * Load user session. 235 * Load user session.
246 * 236 *
@@ -249,7 +239,7 @@ function allIPs()
249function fillSessionInfo($conf) 239function fillSessionInfo($conf)
250{ 240{
251 $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid) 241 $_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // Generate unique random number (different than phpsessionid)
252 $_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked. 242 $_SESSION['ip'] = client_ip_id($_SERVER);
253 $_SESSION['username']= $conf->get('credentials.login'); 243 $_SESSION['username']= $conf->get('credentials.login');
254 $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration. 244 $_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration.
255} 245}
diff --git a/tests/HttpUtils/ClientIpIdTest.php b/tests/HttpUtils/ClientIpIdTest.php
new file mode 100644
index 00000000..c15ac5cc
--- /dev/null
+++ b/tests/HttpUtils/ClientIpIdTest.php
@@ -0,0 +1,52 @@
1<?php
2/**
3 * HttpUtils' tests
4 */
5
6require_once 'application/HttpUtils.php';
7
8/**
9 * Unitary tests for client_ip_id()
10 */
11class ClientIpIdTest extends PHPUnit_Framework_TestCase
12{
13 /**
14 * Get a remote client ID based on its IP
15 */
16 public function testClientIpIdRemote()
17 {
18 $this->assertEquals(
19 '10.1.167.42',
20 client_ip_id(['REMOTE_ADDR' => '10.1.167.42'])
21 );
22 }
23
24 /**
25 * Get a remote client ID based on its IP and proxy information (1)
26 */
27 public function testClientIpIdRemoteForwarded()
28 {
29 $this->assertEquals(
30 '10.1.167.42_127.0.1.47',
31 client_ip_id([
32 'REMOTE_ADDR' => '10.1.167.42',
33 'HTTP_X_FORWARDED_FOR' => '127.0.1.47'
34 ])
35 );
36 }
37
38 /**
39 * Get a remote client ID based on its IP and proxy information (2)
40 */
41 public function testClientIpIdRemoteForwardedClient()
42 {
43 $this->assertEquals(
44 '10.1.167.42_10.1.167.56_127.0.1.47',
45 client_ip_id([
46 'REMOTE_ADDR' => '10.1.167.42',
47 'HTTP_X_FORWARDED_FOR' => '10.1.167.56',
48 'HTTP_CLIENT_IP' => '127.0.1.47'
49 ])
50 );
51 }
52}