]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | namespace Shaarli\Security; | |
3 | ||
4 | use Shaarli\Config\ConfigManager; | |
5 | ||
6 | /** | |
7 | * Manages the server-side session | |
8 | */ | |
9 | class SessionManager | |
10 | { | |
11 | /** @var int Session expiration timeout, in seconds */ | |
12 | public static $SHORT_TIMEOUT = 3600; // 1 hour | |
13 | ||
14 | /** @var int Session expiration timeout, in seconds */ | |
15 | public static $LONG_TIMEOUT = 31536000; // 1 year | |
16 | ||
17 | /** @var array Local reference to the global $_SESSION array */ | |
18 | protected $session = []; | |
19 | ||
20 | /** @var ConfigManager Configuration Manager instance **/ | |
21 | protected $conf = null; | |
22 | ||
23 | /** @var bool Whether the user should stay signed in (LONG_TIMEOUT) */ | |
24 | protected $staySignedIn = false; | |
25 | ||
26 | /** | |
27 | * Constructor | |
28 | * | |
29 | * @param array $session The $_SESSION array (reference) | |
30 | * @param ConfigManager $conf ConfigManager instance | |
31 | */ | |
32 | public function __construct(& $session, $conf) | |
33 | { | |
34 | $this->session = &$session; | |
35 | $this->conf = $conf; | |
36 | } | |
37 | ||
38 | /** | |
39 | * Define whether the user should stay signed in across browser sessions | |
40 | * | |
41 | * @param bool $staySignedIn Keep the user signed in | |
42 | */ | |
43 | public function setStaySignedIn($staySignedIn) | |
44 | { | |
45 | $this->staySignedIn = $staySignedIn; | |
46 | } | |
47 | ||
48 | /** | |
49 | * Generates a session token | |
50 | * | |
51 | * @return string token | |
52 | */ | |
53 | public function generateToken() | |
54 | { | |
55 | $token = sha1(uniqid('', true) .'_'. mt_rand() . $this->conf->get('credentials.salt')); | |
56 | $this->session['tokens'][$token] = 1; | |
57 | return $token; | |
58 | } | |
59 | ||
60 | /** | |
61 | * Checks the validity of a session token, and destroys it afterwards | |
62 | * | |
63 | * @param string $token The token to check | |
64 | * | |
65 | * @return bool true if the token is valid, else false | |
66 | */ | |
67 | public function checkToken($token) | |
68 | { | |
69 | if (! isset($this->session['tokens'][$token])) { | |
70 | // the token is wrong, or has already been used | |
71 | return false; | |
72 | } | |
73 | ||
74 | // destroy the token to prevent future use | |
75 | unset($this->session['tokens'][$token]); | |
76 | return true; | |
77 | } | |
78 | ||
79 | /** | |
80 | * Validate session ID to prevent Full Path Disclosure. | |
81 | * | |
82 | * See #298. | |
83 | * The session ID's format depends on the hash algorithm set in PHP settings | |
84 | * | |
85 | * @param string $sessionId Session ID | |
86 | * | |
87 | * @return true if valid, false otherwise. | |
88 | * | |
89 | * @see http://php.net/manual/en/function.hash-algos.php | |
90 | * @see http://php.net/manual/en/session.configuration.php | |
91 | */ | |
92 | public static function checkId($sessionId) | |
93 | { | |
94 | if (empty($sessionId)) { | |
95 | return false; | |
96 | } | |
97 | ||
98 | if (!$sessionId) { | |
99 | return false; | |
100 | } | |
101 | ||
102 | if (!preg_match('/^[a-zA-Z0-9,-]{2,128}$/', $sessionId)) { | |
103 | return false; | |
104 | } | |
105 | ||
106 | return true; | |
107 | } | |
108 | ||
109 | /** | |
110 | * Store user login information after a successful login | |
111 | * | |
112 | * @param string $clientIpId Client IP address identifier | |
113 | */ | |
114 | public function storeLoginInfo($clientIpId) | |
115 | { | |
116 | $this->session['ip'] = $clientIpId; | |
117 | $this->session['username'] = $this->conf->get('credentials.login'); | |
118 | $this->extendTimeValidityBy(self::$SHORT_TIMEOUT); | |
119 | } | |
120 | ||
121 | /** | |
122 | * Extend session validity | |
123 | */ | |
124 | public function extendSession() | |
125 | { | |
126 | if ($this->staySignedIn) { | |
127 | return $this->extendTimeValidityBy(self::$LONG_TIMEOUT); | |
128 | } | |
129 | return $this->extendTimeValidityBy(self::$SHORT_TIMEOUT); | |
130 | } | |
131 | ||
132 | /** | |
133 | * Extend expiration time | |
134 | * | |
135 | * @param int $duration Expiration time extension (seconds) | |
136 | * | |
137 | * @return int New session expiration time | |
138 | */ | |
139 | protected function extendTimeValidityBy($duration) | |
140 | { | |
141 | $expirationTime = time() + $duration; | |
142 | $this->session['expires_on'] = $expirationTime; | |
143 | return $expirationTime; | |
144 | } | |
145 | ||
146 | /** | |
147 | * Logout a user by unsetting all login information | |
148 | * | |
149 | * See: | |
150 | * - https://secure.php.net/manual/en/function.setcookie.php | |
151 | */ | |
152 | public function logout() | |
153 | { | |
154 | if (isset($this->session)) { | |
155 | unset($this->session['ip']); | |
156 | unset($this->session['expires_on']); | |
157 | unset($this->session['username']); | |
158 | unset($this->session['visibility']); | |
159 | unset($this->session['untaggedonly']); | |
160 | } | |
161 | } | |
162 | ||
163 | /** | |
164 | * Check whether the session has expired | |
165 | * | |
166 | * @param string $clientIpId Client IP address identifier | |
167 | * | |
168 | * @return bool true if the session has expired, false otherwise | |
169 | */ | |
170 | public function hasSessionExpired() | |
171 | { | |
172 | if (empty($this->session['expires_on'])) { | |
173 | return true; | |
174 | } | |
175 | if (time() >= $this->session['expires_on']) { | |
176 | return true; | |
177 | } | |
178 | return false; | |
179 | } | |
180 | ||
181 | /** | |
182 | * Check whether the client IP address has changed | |
183 | * | |
184 | * @param string $clientIpId Client IP address identifier | |
185 | * | |
186 | * @return bool true if the IP has changed, false if it has not, or | |
187 | * if session protection has been disabled | |
188 | */ | |
189 | public function hasClientIpChanged($clientIpId) | |
190 | { | |
191 | if ($this->conf->get('security.session_protection_disabled') === true) { | |
192 | return false; | |
193 | } | |
194 | if (isset($this->session['ip']) && $this->session['ip'] === $clientIpId) { | |
195 | return false; | |
196 | } | |
197 | return true; | |
198 | } | |
199 | } |