]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/3rdparty/Session.class.php
[fix] #115 cookie lifetime was empty
[github/wallabag/wallabag.git] / inc / 3rdparty / Session.class.php
CommitLineData
e4d2565e 1<?php
2/**
3 * Session management class
f6597c7c 4 *
e4d2565e 5 * http://www.developpez.net/forums/d51943/php/langage/sessions/
6 * http://sebsauvage.net/wiki/doku.php?id=php:session
7 * http://sebsauvage.net/wiki/doku.php?id=php:shaarli
f6597c7c 8 *
e4d2565e 9 * Features:
10 * - Everything is stored on server-side (we do not trust client-side data,
11 * such as cookie expiration)
f6597c7c
NL
12 * - IP addresses are checked on each access to prevent session cookie hijacking
13 * (such as Firesheep)
e4d2565e 14 * - Session expires on user inactivity (Session expiration date is
15 * automatically updated everytime the user accesses a page.)
16 * - A unique secret key is generated on server-side for this session
f6597c7c
NL
17 * (and never sent over the wire) which can be used to sign forms (HMAC)
18 * (See $_SESSION['uid'])
19 * - Token management to prevent XSRF attacks
20 * - Brute force protection with ban management
e4d2565e 21 *
f6597c7c
NL
22 * TODOs
23 * - Replace globals with variables in Session class
e4d2565e 24 *
f6597c7c
NL
25 * How to use:
26 * - http://tontof.net/kriss/php5/session
e4d2565e 27 */
e4d2565e 28class Session
29{
f6597c7c
NL
30 // Personnalize PHP session name
31 public static $sessionName = '';
e4d2565e 32 // If the user does not access any page within this time,
f6597c7c 33 // his/her session is considered expired (3600 sec. = 1 hour)
16fd1cce 34 public static $inactivityTimeout = 86400;
a0aa1504 35 // Extra timeout for long sessions (if enabled) (82800 sec. = 23 hours)
71b0d53c 36 public static $longSessionTimeout = 604800; // 604800 = a week
f6597c7c
NL
37 // If you get disconnected often or if your IP address changes often.
38 // Let you disable session cookie hijacking protection
39 public static $disableSessionProtection = false;
40 // Ban IP after this many failures.
41 public static $banAfter = 4;
42 // Ban duration for IP address after login failures (in seconds).
43 // (1800 sec. = 30 minutes)
44 public static $banDuration = 1800;
45 // File storage for failures and bans. If empty, no ban management.
46 public static $banFile = '';
e4d2565e 47
f6597c7c
NL
48 /**
49 * Initialize session
50 */
51 public static function init()
e4d2565e 52 {
f6597c7c
NL
53 // Force cookie path (but do not change lifetime)
54 $cookie = session_get_cookie_params();
55 // Default cookie expiration and path.
56 $cookiedir = '';
57 if (dirname($_SERVER['SCRIPT_NAME'])!='/') {
58 $cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/';
59 }
60 $ssl = false;
61 if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") {
62 $ssl = true;
63 }
71b0d53c 64 session_set_cookie_params(self::$longSessionTimeout, $cookiedir, $_SERVER['HTTP_HOST'], $ssl);
e4d2565e 65 // Use cookies to store session.
66 ini_set('session.use_cookies', 1);
67 // Force cookies for session (phpsessionID forbidden in URL)
68 ini_set('session.use_only_cookies', 1);
f6597c7c 69 if (!session_id()) {
e4d2565e 70 // Prevent php to use sessionID in URL if cookies are disabled.
71 ini_set('session.use_trans_sid', false);
f6597c7c
NL
72 if (!empty(self::$sessionName)) {
73 session_name(self::$sessionName);
74 }
75 session_start();
e4d2565e 76 }
77 }
78
f6597c7c
NL
79 /**
80 * Returns the IP address
81 * (Used to prevent session cookie hijacking.)
82 *
83 * @return string IP addresses
84 */
85 private static function _allIPs()
e4d2565e 86 {
f6597c7c
NL
87 $ip = $_SERVER["REMOTE_ADDR"];
88 $ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : '';
89 $ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : '';
e4d2565e 90
f6597c7c 91 return $ip;
e4d2565e 92 }
93
f6597c7c
NL
94 /**
95 * Check that user/password is correct and then init some SESSION variables.
96 *
97 * @param string $login Login reference
98 * @param string $password Password reference
99 * @param string $loginTest Login to compare with login reference
100 * @param string $passwordTest Password to compare with password reference
101 * @param array $pValues Array of variables to store in SESSION
102 *
103 * @return true|false True if login and password are correct, false
104 * otherwise
105 */
106 public static function login (
107 $login,
108 $password,
109 $loginTest,
110 $passwordTest,
a0aa1504 111 $longlastingsession,
f6597c7c 112 $pValues = array())
e4d2565e 113 {
f6597c7c
NL
114 self::banInit();
115 if (self::banCanLogin()) {
116 if ($login === $loginTest && $password === $passwordTest) {
117 self::banLoginOk();
118 // Generate unique random number to sign forms (HMAC)
119 $_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand());
120 $_SESSION['ip'] = self::_allIPs();
121 $_SESSION['username'] = $login;
122 // Set session expiration.
123 $_SESSION['expires_on'] = time() + self::$inactivityTimeout;
a0aa1504
DS
124 if ($longlastingsession) {
125 $_SESSION['longlastingsession'] = self::$longSessionTimeout;
126 $_SESSION['expires_on'] += $_SESSION['longlastingsession'];
127 }
128
f6597c7c
NL
129 foreach ($pValues as $key => $value) {
130 $_SESSION[$key] = $value;
131 }
132
133 return true;
134 }
135 self::banLoginFailed();
e4d2565e 136 }
f6597c7c 137
e4d2565e 138 return false;
139 }
140
f6597c7c
NL
141 /**
142 * Unset SESSION variable to force logout
143 */
e4d2565e 144 public static function logout()
145 {
71b0d53c
NL
146 // unset($_SESSION['uid'],$_SESSION['ip'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['longlastingsession'], $_SESSION['poche_user']);
147
148 // Destruction du cookie (le code peut paraître complexe mais c'est pour être certain de reprendre les mêmes paramètres)
149 $args = array_merge(array(session_name(), ''), array_values(session_get_cookie_params()));
150 $args[2] = time() - 3600;
151 call_user_func_array('setcookie', $args);
152 // Suppression physique de la session
153 session_destroy();
e4d2565e 154 }
155
f6597c7c
NL
156 /**
157 * Make sure user is logged in.
158 *
159 * @return true|false True if user is logged in, false otherwise
160 */
e4d2565e 161 public static function isLogged()
162 {
163 if (!isset ($_SESSION['uid'])
f6597c7c
NL
164 || (self::$disableSessionProtection === false
165 && $_SESSION['ip'] !== self::_allIPs())
166 || time() >= $_SESSION['expires_on']) {
167 self::logout();
168
e4d2565e 169 return false;
170 }
171 // User accessed a page : Update his/her session expiration date.
f6597c7c
NL
172 $_SESSION['expires_on'] = time() + self::$inactivityTimeout;
173 if (!empty($_SESSION['longlastingsession'])) {
174 $_SESSION['expires_on'] += $_SESSION['longlastingsession'];
175 }
176
e4d2565e 177 return true;
178 }
179
f6597c7c
NL
180 /**
181 * Create a token, store it in SESSION and return it
182 *
183 * @param string $salt to prevent birthday attack
184 *
185 * @return string Token created
186 */
187 public static function getToken($salt = '')
e4d2565e 188 {
f6597c7c 189 if (!isset($_SESSION['tokens'])) {
e4d2565e 190 $_SESSION['tokens']=array();
191 }
192 // We generate a random string and store it on the server side.
f6597c7c 193 $rnd = sha1(uniqid('', true).'_'.mt_rand().$salt);
e4d2565e 194 $_SESSION['tokens'][$rnd]=1;
f6597c7c 195
e4d2565e 196 return $rnd;
197 }
198
f6597c7c
NL
199 /**
200 * Tells if a token is ok. Using this function will destroy the token.
201 *
202 * @param string $token Token to test
203 *
204 * @return true|false True if token is correct, false otherwise
205 */
e4d2565e 206 public static function isToken($token)
207 {
f6597c7c 208 if (isset($_SESSION['tokens'][$token])) {
e4d2565e 209 unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
f6597c7c 210
e4d2565e 211 return true; // Token is ok.
212 }
f6597c7c 213
e4d2565e 214 return false; // Wrong token, or already used.
215 }
f6597c7c
NL
216
217 /**
218 * Signal a failed login. Will ban the IP if too many failures:
219 */
220 public static function banLoginFailed()
221 {
222 if (self::$banFile !== '') {
223 $ip = $_SERVER["REMOTE_ADDR"];
224 $gb = $GLOBALS['IPBANS'];
225
226 if (!isset($gb['FAILURES'][$ip])) {
227 $gb['FAILURES'][$ip] = 0;
228 }
229 $gb['FAILURES'][$ip]++;
230 if ($gb['FAILURES'][$ip] > (self::$banAfter - 1)) {
231 $gb['BANS'][$ip]= time() + self::$banDuration;
232 }
233
234 $GLOBALS['IPBANS'] = $gb;
235 file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
236 }
237 }
238
239 /**
240 * Signals a successful login. Resets failed login counter.
241 */
242 public static function banLoginOk()
243 {
244 if (self::$banFile !== '') {
245 $ip = $_SERVER["REMOTE_ADDR"];
246 $gb = $GLOBALS['IPBANS'];
247 unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
248 $GLOBALS['IPBANS'] = $gb;
249 file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
250 }
251 }
252
253 /**
254 * Ban init
255 */
256 public static function banInit()
257 {
258 if (self::$banFile !== '') {
259 if (!is_file(self::$banFile)) {
260 file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(), 'BANS'=>array()), true).";\n?>");
261 }
262 include self::$banFile;
263 }
264 }
265
266 /**
267 * Checks if the user CAN login. If 'true', the user can try to login.
268 *
269 * @return boolean true if user is banned, false otherwise
270 */
271 public static function banCanLogin()
272 {
273 if (self::$banFile !== '') {
274 $ip = $_SERVER["REMOTE_ADDR"];
275 $gb = $GLOBALS['IPBANS'];
276 if (isset($gb['BANS'][$ip])) {
277 // User is banned. Check if the ban has expired:
278 if ($gb['BANS'][$ip] <= time()) {
279 // Ban expired, user can try to login again.
280 unset($gb['FAILURES'][$ip]);
281 unset($gb['BANS'][$ip]);
282 file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
283
284 return true; // Ban has expired, user can login.
285 }
286
287 return false; // User is banned.
288 }
289 }
290
291 return true; // User is not banned.
292 }
293}