mirror of
https://github.com/moparisthebest/wallabag
synced 2024-11-27 11:22:17 -05:00
fix bug #127: update session class
This commit is contained in:
parent
a8778dc23e
commit
f6597c7cb9
271
inc/3rdparty/Session.class.php
vendored
271
inc/3rdparty/Session.class.php
vendored
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Session management class
|
* Session management class
|
||||||
|
*
|
||||||
* http://www.developpez.net/forums/d51943/php/langage/sessions/
|
* http://www.developpez.net/forums/d51943/php/langage/sessions/
|
||||||
* http://sebsauvage.net/wiki/doku.php?id=php:session
|
* http://sebsauvage.net/wiki/doku.php?id=php:session
|
||||||
* http://sebsauvage.net/wiki/doku.php?id=php:shaarli
|
* http://sebsauvage.net/wiki/doku.php?id=php:shaarli
|
||||||
@ -8,129 +9,271 @@
|
|||||||
* Features:
|
* Features:
|
||||||
* - Everything is stored on server-side (we do not trust client-side data,
|
* - Everything is stored on server-side (we do not trust client-side data,
|
||||||
* such as cookie expiration)
|
* such as cookie expiration)
|
||||||
* - IP addresses + user agent are checked on each access to prevent session
|
* - IP addresses are checked on each access to prevent session cookie hijacking
|
||||||
* cookie hijacking (such as Firesheep)
|
* (such as Firesheep)
|
||||||
* - Session expires on user inactivity (Session expiration date is
|
* - Session expires on user inactivity (Session expiration date is
|
||||||
* automatically updated everytime the user accesses a page.)
|
* automatically updated everytime the user accesses a page.)
|
||||||
* - A unique secret key is generated on server-side for this session
|
* - A unique secret key is generated on server-side for this session
|
||||||
* (and never sent over the wire) which can be used
|
* (and never sent over the wire) which can be used to sign forms (HMAC)
|
||||||
* to sign forms (HMAC) (See $_SESSION['uid'] )
|
* (See $_SESSION['uid'])
|
||||||
* - Token management to prevent XSRF attacks.
|
* - Token management to prevent XSRF attacks
|
||||||
|
* - Brute force protection with ban management
|
||||||
*
|
*
|
||||||
* TODO:
|
* TODOs
|
||||||
* - log login fail
|
* - Replace globals with variables in Session class
|
||||||
* - prevent brute force (ban IP)
|
|
||||||
*
|
*
|
||||||
* HOWTOUSE:
|
* How to use:
|
||||||
* - Just call Session::init(); to initialize session and
|
* - http://tontof.net/kriss/php5/session
|
||||||
* check if connected with Session::isLogged()
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Session
|
class Session
|
||||||
{
|
{
|
||||||
|
// Personnalize PHP session name
|
||||||
|
public static $sessionName = '';
|
||||||
// If the user does not access any page within this time,
|
// If the user does not access any page within this time,
|
||||||
// his/her session is considered expired (in seconds).
|
// his/her session is considered expired (3600 sec. = 1 hour)
|
||||||
public static $inactivity_timeout = 3600;
|
public static $inactivityTimeout = 3600;
|
||||||
private static $_instance;
|
// If you get disconnected often or if your IP address changes often.
|
||||||
|
// Let you disable session cookie hijacking protection
|
||||||
|
public static $disableSessionProtection = false;
|
||||||
|
// Ban IP after this many failures.
|
||||||
|
public static $banAfter = 4;
|
||||||
|
// Ban duration for IP address after login failures (in seconds).
|
||||||
|
// (1800 sec. = 30 minutes)
|
||||||
|
public static $banDuration = 1800;
|
||||||
|
// File storage for failures and bans. If empty, no ban management.
|
||||||
|
public static $banFile = '';
|
||||||
|
|
||||||
// constructor
|
/**
|
||||||
private function __construct()
|
* Initialize session
|
||||||
|
*/
|
||||||
|
public static function init()
|
||||||
{
|
{
|
||||||
|
// Force cookie path (but do not change lifetime)
|
||||||
|
$cookie = session_get_cookie_params();
|
||||||
|
// Default cookie expiration and path.
|
||||||
|
$cookiedir = '';
|
||||||
|
if (dirname($_SERVER['SCRIPT_NAME'])!='/') {
|
||||||
|
$cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/';
|
||||||
|
}
|
||||||
|
$ssl = false;
|
||||||
|
if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") {
|
||||||
|
$ssl = true;
|
||||||
|
}
|
||||||
|
session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['HTTP_HOST'], $ssl);
|
||||||
// Use cookies to store session.
|
// Use cookies to store session.
|
||||||
ini_set('session.use_cookies', 1);
|
ini_set('session.use_cookies', 1);
|
||||||
// Force cookies for session (phpsessionID forbidden in URL)
|
// Force cookies for session (phpsessionID forbidden in URL)
|
||||||
ini_set('session.use_only_cookies', 1);
|
ini_set('session.use_only_cookies', 1);
|
||||||
if (!session_id()){
|
if (!session_id()) {
|
||||||
// Prevent php to use sessionID in URL if cookies are disabled.
|
// Prevent php to use sessionID in URL if cookies are disabled.
|
||||||
ini_set('session.use_trans_sid', false);
|
ini_set('session.use_trans_sid', false);
|
||||||
session_start('poche');
|
if (!empty(self::$sessionName)) {
|
||||||
|
session_name(self::$sessionName);
|
||||||
|
}
|
||||||
|
session_start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize session
|
/**
|
||||||
public static function init()
|
* Returns the IP address
|
||||||
|
* (Used to prevent session cookie hijacking.)
|
||||||
|
*
|
||||||
|
* @return string IP addresses
|
||||||
|
*/
|
||||||
|
private static function _allIPs()
|
||||||
{
|
{
|
||||||
if (!isset(self::$_instance)) {
|
$ip = $_SERVER["REMOTE_ADDR"];
|
||||||
self::$_instance = new Session();
|
$ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : '';
|
||||||
}
|
$ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : '';
|
||||||
|
|
||||||
|
return $ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the IP address, user agent and language of the client
|
/**
|
||||||
// (Used to prevent session cookie hijacking.)
|
* Check that user/password is correct and then init some SESSION variables.
|
||||||
private static function _allInfos()
|
*
|
||||||
{
|
* @param string $login Login reference
|
||||||
$infos = $_SERVER["REMOTE_ADDR"];
|
* @param string $password Password reference
|
||||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
* @param string $loginTest Login to compare with login reference
|
||||||
$infos.=$_SERVER['HTTP_X_FORWARDED_FOR'];
|
* @param string $passwordTest Password to compare with password reference
|
||||||
}
|
* @param array $pValues Array of variables to store in SESSION
|
||||||
if (isset($_SERVER['HTTP_CLIENT_IP'])) {
|
*
|
||||||
$infos.='_'.$_SERVER['HTTP_CLIENT_IP'];
|
* @return true|false True if login and password are correct, false
|
||||||
}
|
* otherwise
|
||||||
$infos.='_'.$_SERVER['HTTP_USER_AGENT'];
|
*/
|
||||||
$infos.='_'.$_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
public static function login (
|
||||||
return sha1($infos);
|
$login,
|
||||||
}
|
$password,
|
||||||
|
$loginTest,
|
||||||
// Check that user/password is correct and init some SESSION variables.
|
$passwordTest,
|
||||||
public static function login($login,$password,$login_test,$password_test,
|
|
||||||
$pValues = array())
|
$pValues = array())
|
||||||
{
|
{
|
||||||
|
self::banInit();
|
||||||
|
if (self::banCanLogin()) {
|
||||||
|
if ($login === $loginTest && $password === $passwordTest) {
|
||||||
|
self::banLoginOk();
|
||||||
|
// Generate unique random number to sign forms (HMAC)
|
||||||
|
$_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand());
|
||||||
|
$_SESSION['ip'] = self::_allIPs();
|
||||||
|
$_SESSION['username'] = $login;
|
||||||
|
// Set session expiration.
|
||||||
|
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
|
||||||
|
|
||||||
foreach ($pValues as $key => $value) {
|
foreach ($pValues as $key => $value) {
|
||||||
$_SESSION[$key] = $value;
|
$_SESSION[$key] = $value;
|
||||||
}
|
}
|
||||||
if ($login==$login_test && $password==$password_test){
|
|
||||||
// generate unique random number to sign forms (HMAC)
|
|
||||||
$_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand());
|
|
||||||
$_SESSION['info']=Session::_allInfos();
|
|
||||||
$_SESSION['username']=$login;
|
|
||||||
// Set session expiration.
|
|
||||||
$_SESSION['expires_on']=time()+Session::$inactivity_timeout;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
self::banLoginFailed();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force logout
|
/**
|
||||||
|
* Unset SESSION variable to force logout
|
||||||
|
*/
|
||||||
public static function logout()
|
public static function logout()
|
||||||
{
|
{
|
||||||
unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']);
|
unset($_SESSION['uid'], $_SESSION['ip'], $_SESSION['expires_on']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure user is logged in.
|
/**
|
||||||
|
* Make sure user is logged in.
|
||||||
|
*
|
||||||
|
* @return true|false True if user is logged in, false otherwise
|
||||||
|
*/
|
||||||
public static function isLogged()
|
public static function isLogged()
|
||||||
{
|
{
|
||||||
if (!isset ($_SESSION['uid'])
|
if (!isset ($_SESSION['uid'])
|
||||||
|| $_SESSION['info']!=Session::_allInfos()
|
|| (self::$disableSessionProtection === false
|
||||||
|| time()>=$_SESSION['expires_on']){
|
&& $_SESSION['ip'] !== self::_allIPs())
|
||||||
Session::logout();
|
|| time() >= $_SESSION['expires_on']) {
|
||||||
|
self::logout();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// User accessed a page : Update his/her session expiration date.
|
// User accessed a page : Update his/her session expiration date.
|
||||||
$_SESSION['expires_on']=time()+Session::$inactivity_timeout;
|
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
|
||||||
|
if (!empty($_SESSION['longlastingsession'])) {
|
||||||
|
$_SESSION['expires_on'] += $_SESSION['longlastingsession'];
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a token.
|
/**
|
||||||
public static function getToken()
|
* Create a token, store it in SESSION and return it
|
||||||
|
*
|
||||||
|
* @param string $salt to prevent birthday attack
|
||||||
|
*
|
||||||
|
* @return string Token created
|
||||||
|
*/
|
||||||
|
public static function getToken($salt = '')
|
||||||
{
|
{
|
||||||
if (!isset($_SESSION['tokens'])){
|
if (!isset($_SESSION['tokens'])) {
|
||||||
$_SESSION['tokens']=array();
|
$_SESSION['tokens']=array();
|
||||||
}
|
}
|
||||||
// We generate a random string and store it on the server side.
|
// We generate a random string and store it on the server side.
|
||||||
$rnd = sha1(uniqid('',true).'_'.mt_rand());
|
$rnd = sha1(uniqid('', true).'_'.mt_rand().$salt);
|
||||||
$_SESSION['tokens'][$rnd]=1;
|
$_SESSION['tokens'][$rnd]=1;
|
||||||
|
|
||||||
return $rnd;
|
return $rnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tells if a token is ok. Using this function will destroy the token.
|
/**
|
||||||
// return true if token is ok.
|
* Tells if a token is ok. Using this function will destroy the token.
|
||||||
|
*
|
||||||
|
* @param string $token Token to test
|
||||||
|
*
|
||||||
|
* @return true|false True if token is correct, false otherwise
|
||||||
|
*/
|
||||||
public static function isToken($token)
|
public static function isToken($token)
|
||||||
{
|
{
|
||||||
if (isset($_SESSION['tokens'][$token]))
|
if (isset($_SESSION['tokens'][$token])) {
|
||||||
{
|
|
||||||
unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
|
unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
|
||||||
|
|
||||||
return true; // Token is ok.
|
return true; // Token is ok.
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // Wrong token, or already used.
|
return false; // Wrong token, or already used.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal a failed login. Will ban the IP if too many failures:
|
||||||
|
*/
|
||||||
|
public static function banLoginFailed()
|
||||||
|
{
|
||||||
|
if (self::$banFile !== '') {
|
||||||
|
$ip = $_SERVER["REMOTE_ADDR"];
|
||||||
|
$gb = $GLOBALS['IPBANS'];
|
||||||
|
|
||||||
|
if (!isset($gb['FAILURES'][$ip])) {
|
||||||
|
$gb['FAILURES'][$ip] = 0;
|
||||||
|
}
|
||||||
|
$gb['FAILURES'][$ip]++;
|
||||||
|
if ($gb['FAILURES'][$ip] > (self::$banAfter - 1)) {
|
||||||
|
$gb['BANS'][$ip]= time() + self::$banDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
$GLOBALS['IPBANS'] = $gb;
|
||||||
|
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals a successful login. Resets failed login counter.
|
||||||
|
*/
|
||||||
|
public static function banLoginOk()
|
||||||
|
{
|
||||||
|
if (self::$banFile !== '') {
|
||||||
|
$ip = $_SERVER["REMOTE_ADDR"];
|
||||||
|
$gb = $GLOBALS['IPBANS'];
|
||||||
|
unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
|
||||||
|
$GLOBALS['IPBANS'] = $gb;
|
||||||
|
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ban init
|
||||||
|
*/
|
||||||
|
public static function banInit()
|
||||||
|
{
|
||||||
|
if (self::$banFile !== '') {
|
||||||
|
if (!is_file(self::$banFile)) {
|
||||||
|
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(), 'BANS'=>array()), true).";\n?>");
|
||||||
|
}
|
||||||
|
include self::$banFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user CAN login. If 'true', the user can try to login.
|
||||||
|
*
|
||||||
|
* @return boolean true if user is banned, false otherwise
|
||||||
|
*/
|
||||||
|
public static function banCanLogin()
|
||||||
|
{
|
||||||
|
if (self::$banFile !== '') {
|
||||||
|
$ip = $_SERVER["REMOTE_ADDR"];
|
||||||
|
$gb = $GLOBALS['IPBANS'];
|
||||||
|
if (isset($gb['BANS'][$ip])) {
|
||||||
|
// User is banned. Check if the ban has expired:
|
||||||
|
if ($gb['BANS'][$ip] <= time()) {
|
||||||
|
// Ban expired, user can try to login again.
|
||||||
|
unset($gb['FAILURES'][$ip]);
|
||||||
|
unset($gb['BANS'][$ip]);
|
||||||
|
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
|
||||||
|
|
||||||
|
return true; // Ban has expired, user can login.
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // User is banned.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // User is not banned.
|
||||||
|
}
|
||||||
}
|
}
|
@ -93,6 +93,7 @@ class Poche
|
|||||||
private function init()
|
private function init()
|
||||||
{
|
{
|
||||||
Tools::initPhp();
|
Tools::initPhp();
|
||||||
|
Session::$sessionName = 'poche';
|
||||||
Session::init();
|
Session::init();
|
||||||
|
|
||||||
if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
|
if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
|
||||||
|
@ -49,3 +49,11 @@ if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timez
|
|||||||
}
|
}
|
||||||
|
|
||||||
$poche = new Poche();
|
$poche = new Poche();
|
||||||
|
|
||||||
|
#XSRF protection with token
|
||||||
|
if (!empty($_POST)) {
|
||||||
|
if (!Session::isToken($_POST['token'])) {
|
||||||
|
die(_('Wrong token'));
|
||||||
|
}
|
||||||
|
unset($_SESSION['tokens']);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user