added: possibility to ban if a session cookie is incorrect (when we are using encoded cookies)
added: possibility to ban if a client tries to hijack the session cookie
added: possibility to ban if a client did not send a session cookie
renamed: ezc functions:
login_cannot_login -> ipban_is_login_allowed_from_this_ip (and the return value was changed)
login_when_available_login -> ipban_current_ip_expires_time
added: config options:
// after how many broken encoded cookie we should ban the current IP
// default: 2 (value in the range <0 - 65535>)
size_t broken_encoded_cookie_treshold;
// after how many incorrect session identifiers (or session indices) we should ban the current IP
// do not set this value too low, as people connecting from the same IP address (from behind a NAT)
// would be banned if they have an old session cookie remembered in the browser
// default: 128 (value in the range <0 - 65535>)
size_t session_hijacking_treshold;
// after how many times a client will be banned if it did not send a session cookie
// default: 1000 (value in the range <0 - 65535>)
size_t no_session_cookie_treshold;
git-svn-id: svn://ttmath.org/publicrep/winix/trunk@995 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
@@ -634,8 +634,6 @@ void App::Make()
|
||||
if( cur.session->ip_ban && cur.session->ip_ban->IsIPBanned() )
|
||||
{
|
||||
PT::Date date(cur.session->ip_ban->expires);
|
||||
|
||||
log << log2 << "App: this IP address is banned until to: " << date << " UTC" << logend;
|
||||
slog << logerror << T("this_ip_is_banned_until") << ' ' << date << " UTC" << logend;
|
||||
|
||||
cur.request->status = WINIX_ERR_PERMISSION_DENIED;
|
||||
|
||||
@@ -218,9 +218,12 @@ void Config::AssignValues(bool stdout_is_closed)
|
||||
session_max = Size(L"session_max", 1000000);
|
||||
session_cookie_encode = Bool(L"session_cookie_encode", false);
|
||||
session_keys_file = Text(L"session_keys_file");
|
||||
session_allow_index_difference = Size(L"session_allow_index_difference", 8);
|
||||
session_index_time_increment = Long(L"session_index_time_increment", 30);
|
||||
session_key_renew_time = Size(L"session_key_renew_time", 172800); // 2 days
|
||||
session_allow_index_difference = Size(L"session_allow_index_difference", 8);
|
||||
session_index_time_increment = Long(L"session_index_time_increment", 30);
|
||||
session_key_renew_time = Size(L"session_key_renew_time", 172800); // 2 days
|
||||
broken_encoded_cookie_treshold = Size(L"broken_encoded_cookie_treshold", 2);
|
||||
session_hijacking_treshold = Size(L"session_hijacking_treshold", 128);
|
||||
no_session_cookie_treshold = Size(L"no_session_cookie_treshold", 1000);
|
||||
|
||||
compression = Bool(L"compression", true);
|
||||
compression_page_min_size = Size(L"compression_page_min_size", 512);
|
||||
@@ -299,6 +302,7 @@ void Config::AssignValues(bool stdout_is_closed)
|
||||
incorrect_login_cannot_login_treshold = Size(L"incorrect_login_cannot_login_treshold", 20);
|
||||
incorrect_login_cannot_login_delay = Size(L"incorrect_login_cannot_login_delay", 1800);
|
||||
|
||||
|
||||
pid_file = Text(L"pid_file", L"");
|
||||
}
|
||||
|
||||
|
||||
@@ -233,6 +233,20 @@ public:
|
||||
// default: 172800 = 2 days (max: 2678400 = 1 month, min: 10)
|
||||
size_t session_key_renew_time;
|
||||
|
||||
// after how many broken encoded cookie we should ban the current IP
|
||||
// default: 2 (value in the range <0 - 65535>)
|
||||
size_t broken_encoded_cookie_treshold;
|
||||
|
||||
// after how many incorrect session identifiers (or session indices) we should ban the current IP
|
||||
// do not set this value too low, as people connecting from the same IP address (from behind a NAT)
|
||||
// would be banned if they have an old session cookie remembered in the browser
|
||||
// default: 128 (value in the range <0 - 65535>)
|
||||
size_t session_hijacking_treshold;
|
||||
|
||||
// after how many times a client will be banned if it did not send a session cookie
|
||||
// default: 1000 (value in the range <0 - 65535>)
|
||||
size_t no_session_cookie_treshold;
|
||||
|
||||
// allow the winix output to be compressed
|
||||
// default: true
|
||||
bool compression;
|
||||
@@ -672,7 +686,7 @@ public:
|
||||
|
||||
// how many incorrect logins there must have been passed to display a captcha
|
||||
// next to the login form
|
||||
// default: 3
|
||||
// default: 3 (value in the range <0 - 65535>)
|
||||
size_t incorrect_login_captcha_treshold;
|
||||
|
||||
// the way how we prevent to login if there are too many incorrect login attempts
|
||||
@@ -683,7 +697,7 @@ public:
|
||||
int incorrect_login_cannot_login_mode;
|
||||
|
||||
// after how many incorrect login attempts we do the incorrect_login_cannot_login_mode action
|
||||
// default: 20
|
||||
// default: 20 (value in the range <0 - 65535>)
|
||||
size_t incorrect_login_cannot_login_treshold;
|
||||
|
||||
// used when incorrect_login_cannot_login_mode is zero
|
||||
|
||||
28
core/ipban.h
28
core/ipban.h
@@ -87,11 +87,21 @@ struct IPBan
|
||||
// the ban level to a greater value
|
||||
time_t expires;
|
||||
|
||||
|
||||
// how many incorrect login attempts there are
|
||||
unsigned int incorrect_login_events;
|
||||
unsigned short int incorrect_login_events;
|
||||
|
||||
// in the future there can be more *_events fields
|
||||
// how many incorrect encoded cookie were sent
|
||||
// only used if config.session_cookie_encode is true and session_keys_file is defined
|
||||
unsigned short int broken_encoded_cookie_events;
|
||||
|
||||
// how many incorrect session identifiers were sent
|
||||
unsigned short int session_hijacking_events;
|
||||
|
||||
// client didn't send a session cookie
|
||||
// it can be a bot or just someone wants to DOS the server
|
||||
// (a new session will be create)
|
||||
unsigned short int no_session_cookie_events;
|
||||
|
||||
|
||||
bool HasFlag(int flag) const
|
||||
@@ -123,7 +133,7 @@ struct IPBan
|
||||
}
|
||||
|
||||
|
||||
void AddNextBanLevel(time_t level1_expires, time_t level2_expires, time_t level3_expires)
|
||||
void IncrementBanLevel(time_t level1_expires, time_t level2_expires, time_t level3_expires)
|
||||
{
|
||||
if( HasFlag(WINIX_IPBAN_FLAG_BAN_LEVEL3) )
|
||||
{
|
||||
@@ -164,14 +174,20 @@ struct IPBan
|
||||
flags = 0;
|
||||
last_used = 0;
|
||||
expires = 0;
|
||||
incorrect_login_events = 0;
|
||||
incorrect_login_events = 0;
|
||||
broken_encoded_cookie_events = 0;
|
||||
session_hijacking_events = 0;
|
||||
no_session_cookie_events = 0;
|
||||
}
|
||||
|
||||
|
||||
void ClearAfterRemovingBan()
|
||||
void ResetEventsCounters()
|
||||
{
|
||||
ClearFlag(WINIX_IPBAN_FLAG_ACTIVE);
|
||||
incorrect_login_events = 0;
|
||||
incorrect_login_events = 0;
|
||||
broken_encoded_cookie_events = 0;
|
||||
session_hijacking_events = 0;
|
||||
no_session_cookie_events = 0;
|
||||
expires = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,9 @@ bool SessionManager::EncodeSessionId(long id, unsigned int index, std::wstring &
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IMPROVE ME we need a better algorithm
|
||||
*/
|
||||
long SessionManager::CreateSessionId()
|
||||
{
|
||||
long id;
|
||||
@@ -179,6 +182,7 @@ SessionContainer::Iterator i = session_tab.End();
|
||||
|
||||
if( i != session_tab.End() )
|
||||
{
|
||||
is_session_set = true;
|
||||
session = &(*i);
|
||||
session->new_session = true;
|
||||
session->SetTimesTo(cur->request->start_time);
|
||||
@@ -190,7 +194,7 @@ SessionContainer::Iterator i = session_tab.End();
|
||||
else
|
||||
{
|
||||
// there is a problem with generating a new session id
|
||||
log << log1 << "SM: cannot create a session id (temporary used: with id 0)" << logend;
|
||||
log << log1 << "SM: cannot create a session id" << logend;
|
||||
SetTemporarySession();
|
||||
}
|
||||
}
|
||||
@@ -198,14 +202,18 @@ SessionContainer::Iterator i = session_tab.End();
|
||||
|
||||
void SessionManager::SetTemporarySession()
|
||||
{
|
||||
is_session_set = true;
|
||||
|
||||
session = &temporary_session;
|
||||
session->Clear(false);
|
||||
session->SetTimesTo(cur->request->start_time);
|
||||
session->new_session = false; // temporary session was initialized at the beginning
|
||||
|
||||
log << log2 << "SM: using temporary session" << logend;
|
||||
}
|
||||
|
||||
|
||||
unsigned int SessionManager::SetSessionCalcDifference(Session & ses, unsigned int index)
|
||||
unsigned int SessionManager::CalculateIndexDifference(Session & ses, unsigned int index)
|
||||
{
|
||||
unsigned int difference;
|
||||
|
||||
@@ -233,15 +241,103 @@ void SessionManager::SetSessionPutLogInfo(Session & ses, bool has_index, unsigne
|
||||
|
||||
|
||||
|
||||
bool SessionManager::SetSessionFromCookie(long id, bool has_index, unsigned int index)
|
||||
void SessionManager::IncrementBanLevel(IPBan * ip_ban)
|
||||
{
|
||||
unsigned int difference = 0;
|
||||
ip_ban->SetFlag(WINIX_IPBAN_FLAG_ACTIVE);
|
||||
|
||||
SessionContainer::Iterator s = session_tab.FindById(id);
|
||||
ip_ban->IncrementBanLevel(cur->request->start_time + (time_t)config->ban_level_1_delay,
|
||||
cur->request->start_time + (time_t)config->ban_level_2_delay,
|
||||
cur->request->start_time + (time_t)config->ban_level_3_delay);
|
||||
|
||||
PT::Date date(ip_ban->expires);
|
||||
log << log2 << "SM: this IP address has been banned to: " << date << " UTC" << logend;
|
||||
}
|
||||
|
||||
|
||||
void SessionManager::SetFirstExpirationTime(IPBan * ip_ban)
|
||||
{
|
||||
time_t expiry = cur->request->start_time + (time_t)config->ban_level_1_delay;
|
||||
|
||||
if( ip_ban->expires < expiry )
|
||||
ip_ban->expires = expiry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SessionManager::BrokenCookieCheckBan()
|
||||
{
|
||||
if( !current_ip_ban )
|
||||
current_ip_ban = &AddIPToBanList(cur->request->ip, cur->request->start_time);
|
||||
|
||||
if( current_ip_ban->broken_encoded_cookie_events < config->broken_encoded_cookie_treshold )
|
||||
{
|
||||
current_ip_ban->broken_encoded_cookie_events += 1;
|
||||
SetFirstExpirationTime(current_ip_ban);
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log2 << "SM: too many incorrect encoded cookies were sent from this IP" << logend;
|
||||
IncrementBanLevel(current_ip_ban);
|
||||
SetTemporarySession();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SessionManager::IncorrectSessionCheckBan()
|
||||
{
|
||||
if( !current_ip_ban )
|
||||
current_ip_ban = &AddIPToBanList(cur->request->ip, cur->request->start_time);
|
||||
|
||||
if( current_ip_ban->session_hijacking_events < config->session_hijacking_treshold )
|
||||
{
|
||||
current_ip_ban->session_hijacking_events += 1;
|
||||
SetFirstExpirationTime(current_ip_ban);
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log2 << "SM: too many incorrect sessions identifiers were sent from this IP" << logend;
|
||||
IncrementBanLevel(current_ip_ban);
|
||||
SetTemporarySession();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SessionManager::NoSessionCookieCheckBan()
|
||||
{
|
||||
if( !current_ip_ban )
|
||||
current_ip_ban = &AddIPToBanList(cur->request->ip, cur->request->start_time);
|
||||
|
||||
if( current_ip_ban->no_session_cookie_events < config->no_session_cookie_treshold )
|
||||
{
|
||||
current_ip_ban->no_session_cookie_events += 1;
|
||||
SetFirstExpirationTime(current_ip_ban);
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log2 << "SM: too many times you have not sent a session cookie" << logend;
|
||||
IncrementBanLevel(current_ip_ban);
|
||||
SetTemporarySession();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool SessionManager::IsSessionCorrect(long id, bool has_index, unsigned int index,
|
||||
const SessionContainer::Iterator & s, unsigned int & difference)
|
||||
{
|
||||
difference = 0;
|
||||
|
||||
if( id == 0 )
|
||||
{
|
||||
log << log3 << "SM: id 0 is reserved for the temporary session" << logend;
|
||||
IncorrectSessionCheckBan();
|
||||
return false;
|
||||
}
|
||||
|
||||
if( s == session_tab.End() )
|
||||
{
|
||||
log << log3 << "SM: there is no a session with id: " << id << logend;
|
||||
IncorrectSessionCheckBan();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,34 +349,51 @@ unsigned int difference = 0;
|
||||
|
||||
if( has_index )
|
||||
{
|
||||
difference = SetSessionCalcDifference(*s, index);
|
||||
difference = CalculateIndexDifference(*s, index);
|
||||
|
||||
if( (size_t)difference > config->session_allow_index_difference )
|
||||
{
|
||||
log << log2 << "SM: an incorrect session index for session: " << id
|
||||
<< ", index difference: " << (size_t)difference << logend;
|
||||
|
||||
IncorrectSessionCheckBan();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// that session is in the table
|
||||
session = &(*s);
|
||||
session->new_session = false;
|
||||
session->last_time = cur->request->start_time;
|
||||
session->last_date = cur->request->start_date;
|
||||
return true;
|
||||
}
|
||||
|
||||
if( session->id_index_changed + config->session_index_time_increment < cur->request->start_time )
|
||||
|
||||
bool SessionManager::SetSessionFromCookie(long id, bool has_index, unsigned int index)
|
||||
{
|
||||
unsigned int difference;
|
||||
bool is_session_correct;
|
||||
|
||||
SessionContainer::Iterator s = session_tab.FindById(id);
|
||||
is_session_correct = IsSessionCorrect(id, has_index, index, s, difference);
|
||||
|
||||
if( is_session_correct )
|
||||
{
|
||||
session->id_index += 1;
|
||||
session->id_index_changed = cur->request->start_time;
|
||||
is_session_set = true;
|
||||
session = &(*s);
|
||||
session->new_session = false;
|
||||
session->last_time = cur->request->start_time;
|
||||
session->last_date = cur->request->start_date;
|
||||
|
||||
if( session->id_index_changed + config->session_index_time_increment < cur->request->start_time )
|
||||
{
|
||||
session->id_index += 1;
|
||||
session->id_index_changed = cur->request->start_time;
|
||||
}
|
||||
|
||||
if( cur->request->method == Request::get )
|
||||
session->last_time_get = cur->request->start_time;
|
||||
|
||||
SetSessionPutLogInfo(*session, has_index, difference);
|
||||
}
|
||||
|
||||
if( cur->request->method == Request::get )
|
||||
session->last_time_get = cur->request->start_time;
|
||||
|
||||
SetSessionPutLogInfo(*session, has_index, difference);
|
||||
|
||||
return true;
|
||||
return is_session_correct;
|
||||
}
|
||||
|
||||
|
||||
@@ -296,6 +409,7 @@ bool SessionManager::SetSessionFromCookie(const std::wstring & cookie)
|
||||
if( !session_id_manager.DecodeToken(cookie, id, index) )
|
||||
{
|
||||
log << log2 << "SM: an incorrect cookie string was sent" << logend;
|
||||
BrokenCookieCheckBan();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -309,45 +423,52 @@ bool SessionManager::SetSessionFromCookie(const std::wstring & cookie)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SessionManager::SetSession()
|
||||
bool SessionManager::IsIPBanned()
|
||||
{
|
||||
current_ip_ban = ban_tab.FindIP(cur->request->ip);
|
||||
|
||||
if( current_ip_ban && current_ip_ban->IsIPBanned() )
|
||||
if( current_ip_ban )
|
||||
{
|
||||
if( current_ip_ban->expires != 0 && cur->request->start_time >= current_ip_ban->expires )
|
||||
{
|
||||
log << log2 << "SM: removing a ban from this IP and resetting events counter" << logend;
|
||||
current_ip_ban->ClearAfterRemovingBan();
|
||||
log << log2 << "SM: resetting events counters for this IP" << logend;
|
||||
current_ip_ban->ResetEventsCounters();
|
||||
}
|
||||
else
|
||||
if( current_ip_ban->IsIPBanned() )
|
||||
{
|
||||
PT::Date date = current_ip_ban->expires;
|
||||
log << log2 << "SM: this ip is bannned to: " << date << logend;
|
||||
SetTemporarySession();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SessionManager::SetSession()
|
||||
{
|
||||
is_session_set = false;
|
||||
|
||||
if( !IsIPBanned() )
|
||||
{
|
||||
CookieTab::iterator i = cur->request->cookie_tab.find(config->http_session_id_name);
|
||||
|
||||
if( i != cur->request->cookie_tab.end() )
|
||||
{
|
||||
if( !SetSessionFromCookie(i->second) )
|
||||
cur->request->cookie_tab.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
log << log2 << "SM: this ip is bannned, using a temporary session" << logend;
|
||||
SetTemporarySession();
|
||||
session->ip_ban = current_ip_ban;
|
||||
return;
|
||||
NoSessionCookieCheckBan();
|
||||
}
|
||||
}
|
||||
|
||||
CookieTab::iterator i = cur->request->cookie_tab.find(config->http_session_id_name);
|
||||
|
||||
if( i == cur->request->cookie_tab.end() )
|
||||
{
|
||||
if( !is_session_set )
|
||||
CreateSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !SetSessionFromCookie(i->second) )
|
||||
{
|
||||
// there is no such a session
|
||||
// deleting the old cookie
|
||||
cur->request->cookie_tab.erase(i);
|
||||
|
||||
// and creating a new one
|
||||
CreateSession();
|
||||
}
|
||||
}
|
||||
|
||||
session->ip_ban = current_ip_ban;
|
||||
}
|
||||
@@ -578,6 +699,15 @@ IPBan & SessionManager::AddIPToBanList(int ip)
|
||||
}
|
||||
|
||||
|
||||
IPBan & SessionManager::AddIPToBanList(int ip, time_t cur_time)
|
||||
{
|
||||
IPBan & ban = ban_tab.AddIP(ip);
|
||||
ban.last_used = cur_time;
|
||||
|
||||
return ban;
|
||||
}
|
||||
|
||||
|
||||
size_t SessionManager::BanListSize()
|
||||
{
|
||||
return ban_tab.Size();
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
void DeleteSessions(); // deleting all sessions
|
||||
bool ChangeSessionId(long old_id);
|
||||
|
||||
void IncrementBanLevel(IPBan * ip_ban);
|
||||
|
||||
void InitTmpSession();
|
||||
void InitBanList();
|
||||
void InitCookieEncoding();
|
||||
@@ -93,6 +95,7 @@ public:
|
||||
size_t MarkAllSessionsToRemove(long user_id);
|
||||
|
||||
IPBan & AddIPToBanList(int ip);
|
||||
IPBan & AddIPToBanList(int ip, time_t cur_time);
|
||||
size_t BanListSize();
|
||||
IPBan & GetIPBan(size_t index);
|
||||
void RemoveIPBan(int ip);
|
||||
@@ -107,34 +110,35 @@ private:
|
||||
Config * config;
|
||||
Cur * cur;
|
||||
System * system;
|
||||
|
||||
LastContainer * last_container;
|
||||
|
||||
// current session - set by SetSession()
|
||||
Session * session;
|
||||
|
||||
SessionContainer session_tab;
|
||||
IPBanContainer ban_tab;
|
||||
|
||||
IPBan * current_ip_ban;
|
||||
|
||||
// session with id 0
|
||||
bool is_session_set;
|
||||
Session temporary_session;
|
||||
|
||||
SessionIdManager session_id_manager;
|
||||
|
||||
bool IsSession(long s);
|
||||
|
||||
|
||||
long CreateSessionId();
|
||||
void CreateSession();
|
||||
bool IsSessionCorrect(long id, bool has_index, unsigned int index, const SessionContainer::Iterator & s, unsigned int & difference);
|
||||
bool SetSessionFromCookie(long id, bool has_index, unsigned int index);
|
||||
bool SetSessionFromCookie(const std::wstring & cookie);
|
||||
void SetTemporarySession();
|
||||
unsigned int SetSessionCalcDifference(Session & ses, unsigned int index);
|
||||
unsigned int CalculateIndexDifference(Session & ses, unsigned int index);
|
||||
void SetSessionPutLogInfo(Session & ses, bool has_index, unsigned int difference);
|
||||
bool IsIPBanned();
|
||||
void SetFirstExpirationTime(IPBan * ip_ban);
|
||||
void BrokenCookieCheckBan();
|
||||
void IncorrectSessionCheckBan();
|
||||
void NoSessionCookieCheckBan();
|
||||
|
||||
// second thread
|
||||
|
||||
/*
|
||||
* second thread
|
||||
*/
|
||||
int deleted;
|
||||
virtual void Work();
|
||||
void CheckSession(SessionContainer::Iterator & i);
|
||||
|
||||
Reference in New Issue
Block a user