complete session timeout, found bug with session manager mutex and fix, found bug with long email verification code and fixed

This commit is contained in:
Dario 2019-10-03 11:33:15 +02:00
parent 3de1e21259
commit cb47fe568e
14 changed files with 306 additions and 74 deletions

View File

@ -36,6 +36,12 @@ void LoginPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::
response.redirect(uri_start + "/"); response.redirect(uri_start + "/");
return; return;
} }
} else {
// remove old cookies if exist
auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId))
// max age of 0 delete cookie
keks.setMaxAge(0);
response.addCookie(keks);
} }
std::ostream& _responseStream = response.send(); std::ostream& _responseStream = response.send();
@ -72,7 +78,7 @@ void LoginPage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::
responseStream << "\t<div class=\"grd_container\">\n"; responseStream << "\t<div class=\"grd_container\">\n";
responseStream << "\t\t<h1>Login</h1>\n"; responseStream << "\t\t<h1>Login</h1>\n";
responseStream << "\t\t"; responseStream << "\t\t";
#line 56 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\login.cpsp" #line 62 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\login.cpsp"
responseStream << ( session->getErrorsHtml() ); responseStream << ( session->getErrorsHtml() );
responseStream << "\n"; responseStream << "\n";
responseStream << "\t\t<fieldset class=\"grd_container_small\">\n"; responseStream << "\t\t<fieldset class=\"grd_container_small\">\n";

View File

@ -28,10 +28,12 @@ Poco::Net::HTTPRequestHandler* PageRequestHandlerFactory::createRequestHandler(c
std::string url_first_part; std::string url_first_part;
mRemoveGETParameters.extract(uri, url_first_part); mRemoveGETParameters.extract(uri, url_first_part);
printf("[PageRequestHandlerFactory] uri: %s, first part: %s\n", uri.data(), url_first_part.data()); if (uri != "/favicon.ico") {
auto referer = request.find("Referer"); printf("[PageRequestHandlerFactory] uri: %s, first part: %s\n", uri.data(), url_first_part.data());
if (referer != request.end()) { auto referer = request.find("Referer");
printf("referer: %s\n", referer->second.data()); if (referer != request.end()) {
printf("referer: %s\n", referer->second.data());
}
} }
// check if user has valid session // check if user has valid session
@ -41,18 +43,25 @@ Poco::Net::HTTPRequestHandler* PageRequestHandlerFactory::createRequestHandler(c
int session_id = 0; int session_id = 0;
try { try {
session_id = atoi(cookies.get("user").data()); session_id = atoi(cookies.get("GRADIDO_LOGIN").data());
} catch (...) {} } catch (...) {}
auto sm = SessionManager::getInstance(); auto sm = SessionManager::getInstance();
auto s = sm->getSession(session_id); auto s = sm->getSession(session_id);
// TODO: count invalid session requests from IP and block IP for some time to prevent brute force session hijacking
// or use log file and configure fail2ban for this to do
if (url_first_part == "/checkEmail") { if (url_first_part == "/checkEmail") {
//return new CheckEmailPage(s); //return new CheckEmailPage(s);
return handleCheckEmail(s, uri, request); if (!s || s->getSessionState() < SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED) {
return handleCheckEmail(s, uri, request);
}
} }
if (s) { if (s) {
if(url_first_part == "/logout") { if(url_first_part == "/logout") {
sm->releseSession(s); sm->releseSession(s);
// remove cookie
printf("session released\n"); printf("session released\n");
return new LoginPage; return new LoginPage;
} }
@ -97,15 +106,26 @@ Poco::Net::HTTPRequestHandler* PageRequestHandlerFactory::handleCheckEmail(Sessi
// try to get code from form get parameter // try to get code from form get parameter
if (!form.empty()) { if (!form.empty()) {
try { try {
verificationCode = stoll(form.get("email-verification-code", "0")); verificationCode = stoull(form.get("email-verification-code", "0"));
} catch (...) {} } catch (...) {}
} }
// try to get code from uri parameter // try to get code from uri parameter
if (!verificationCode) { if (!verificationCode) {
size_t pos = uri.find_last_of("/"); size_t pos = uri.find_last_of("/");
try { try {
verificationCode = stoll(uri.substr(pos + 1)); auto str = uri.substr(pos + 1);
} catch (...) {} verificationCode = stoull(uri.substr(pos + 1));
} catch (const std::invalid_argument& ia) {
std::cerr << "Invalid argument: " << ia.what() << '\n';
} catch (const std::out_of_range& oor) {
std::cerr << "Out of Range error: " << oor.what() << '\n';
}
catch (const std::logic_error & ler) {
std::cerr << "Logical error: " << ler.what() << '\n';
}
catch (...) {
std::cerr << "Unknown error" << '\n';
}
} }
// if no verification code given or error with given code, show form // if no verification code given or error with given code, show form
@ -138,6 +158,9 @@ Poco::Net::HTTPRequestHandler* PageRequestHandlerFactory::handleCheckEmail(Sessi
} }
// suitable session found or created // suitable session found or created
if (session) { if (session) {
auto user_host = request.clientAddress().host();
session->setClientIp(user_host);
// update session, mark as verified // update session, mark as verified
if (session->updateEmailVerification(verificationCode)) { if (session->updateEmailVerification(verificationCode)) {
return new PassphrasePage(session); return new PassphrasePage(session);

View File

@ -35,10 +35,7 @@ void PassphrasePage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::
bool hasErrors = mSession->errorCount() > 0; bool hasErrors = mSession->errorCount() > 0;
// save login cookie, because maybe we've get an new session // save login cookie, because maybe we've get an new session
auto cookie_id = mSession->getHandle(); response.addCookie(mSession->getLoginCookie());
auto user_host = request.clientAddress().host();
mSession->setClientIp(user_host);
response.addCookie(Poco::Net::HTTPCookie("user", std::to_string(cookie_id)));
if (!form.empty()) { if (!form.empty()) {
auto registerKeyChoice = form.get("passphrase", ""); auto registerKeyChoice = form.get("passphrase", "");
@ -97,18 +94,18 @@ void PassphrasePage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::
responseStream << "<body>\n"; responseStream << "<body>\n";
responseStream << "<div class=\"grd_container\">\n"; responseStream << "<div class=\"grd_container\">\n";
responseStream << "\t"; responseStream << "\t";
#line 78 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 75 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
if(mSession && hasErrors) { responseStream << "\n"; if(mSession && hasErrors) { responseStream << "\n";
responseStream << "\t\t"; responseStream << "\t\t";
#line 79 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 76 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
responseStream << ( mSession->getErrorsHtml() ); responseStream << ( mSession->getErrorsHtml() );
responseStream << "\n"; responseStream << "\n";
responseStream << "\t"; responseStream << "\t";
#line 80 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 77 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
} responseStream << "\n"; } responseStream << "\n";
responseStream << "\t<h1>Einen neuen Account anlegen</h1>\n"; responseStream << "\t<h1>Einen neuen Account anlegen</h1>\n";
responseStream << "\t"; responseStream << "\t";
#line 82 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 79 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
if(state == PAGE_SHOW_PASSPHRASE) { responseStream << "\n"; if(state == PAGE_SHOW_PASSPHRASE) { responseStream << "\n";
responseStream << "\t\t<div class=\"grd_text-max-width\">\n"; responseStream << "\t\t<div class=\"grd_text-max-width\">\n";
responseStream << "\t\t\t<div class=\"grd_text\">\n"; responseStream << "\t\t\t<div class=\"grd_text\">\n";
@ -116,16 +113,16 @@ void PassphrasePage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::
responseStream << "\t\t\t</div>\n"; responseStream << "\t\t\t</div>\n";
responseStream << "\t\t\t<div class=\"grd_textarea\">\n"; responseStream << "\t\t\t<div class=\"grd_textarea\">\n";
responseStream << "\t\t\t\t"; responseStream << "\t\t\t\t";
#line 88 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 85 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
responseStream << ( mSession->getPassphrase() ); responseStream << ( mSession->getPassphrase() );
responseStream << "\n"; responseStream << "\n";
responseStream << "\t\t\t</div>\n"; responseStream << "\t\t\t</div>\n";
responseStream << "\t\t\t<a href=\"/saveKeys\">Weiter</a>\n"; responseStream << "\t\t\t<a href=\"/saveKeys\">Weiter</a>\n";
responseStream << "\t\t</div>\n"; responseStream << "\t\t</div>\n";
responseStream << "\t"; responseStream << "\t";
#line 92 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 89 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
} else if(state == PAGE_ASK_PASSPHRASE) { responseStream << "\n"; } else if(state == PAGE_ASK_PASSPHRASE) { responseStream << "\n";
responseStream << "\t<form method=\"POST\">\n"; responseStream << "\t<form method=\"POST\" action=\"/passphrase\">\n";
responseStream << "\t\t<fieldset class=\"grd_container_small\">\n"; responseStream << "\t\t<fieldset class=\"grd_container_small\">\n";
responseStream << "\t\t\t<legend>Neue Gradido Adresse anlegen / wiederherstellen</legend>\n"; responseStream << "\t\t\t<legend>Neue Gradido Adresse anlegen / wiederherstellen</legend>\n";
responseStream << "\t\t\t<p>Hast du schonmal ein Gradido Konto besessen?</p>\n"; responseStream << "\t\t\t<p>Hast du schonmal ein Gradido Konto besessen?</p>\n";
@ -138,7 +135,7 @@ void PassphrasePage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::
responseStream << "\t\t\t\t<label class=\"grd_radio_label\" for=\"passphrase-new-no\">Ja, bitte wiederherstellen!</label>\n"; responseStream << "\t\t\t\t<label class=\"grd_radio_label\" for=\"passphrase-new-no\">Ja, bitte wiederherstellen!</label>\n";
responseStream << "\t\t\t</p>\n"; responseStream << "\t\t\t</p>\n";
responseStream << "\t\t\t<textarea style=\"width:100%;height:100px\" name=\"passphrase-existing\">"; responseStream << "\t\t\t<textarea style=\"width:100%;height:100px\" name=\"passphrase-existing\">";
#line 105 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 102 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
responseStream << ( !form.empty() ? form.get("passphrase-existing", "") : "" ); responseStream << ( !form.empty() ? form.get("passphrase-existing", "") : "" );
responseStream << "</textarea>\n"; responseStream << "</textarea>\n";
responseStream << "\t\t</fieldset>\n"; responseStream << "\t\t</fieldset>\n";
@ -146,13 +143,13 @@ void PassphrasePage::handleRequest(Poco::Net::HTTPServerRequest& request, Poco::
responseStream << "\t\t\n"; responseStream << "\t\t\n";
responseStream << "\t</form>\n"; responseStream << "\t</form>\n";
responseStream << "\t"; responseStream << "\t";
#line 110 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 107 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
} else { responseStream << "\n"; } else { responseStream << "\n";
responseStream << "\t\t<div class=\"grd_text\">\n"; responseStream << "\t\t<div class=\"grd_text\">\n";
responseStream << "\t\t\tUngültige Seite, wenn du das siehst stimmt hier etwas nicht. Bitte wende dich an den Server-Admin. \n"; responseStream << "\t\t\tUngültige Seite, wenn du das siehst stimmt hier etwas nicht. Bitte wende dich an den Server-Admin. \n";
responseStream << "\t\t</div>\n"; responseStream << "\t\t</div>\n";
responseStream << "\t"; responseStream << "\t";
#line 114 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp" #line 111 "I:\\Code\\C++\\Eigene_Projekte\\Gradido_LoginServer\\src\\cpsp\\passphrase.cpsp"
} responseStream << "\n"; } responseStream << "\n";
responseStream << "</div>\n"; responseStream << "</div>\n";
responseStream << "</body>\n"; responseStream << "</body>\n";

View File

@ -17,12 +17,16 @@ using Poco::Net::ConsoleCertificateHandler;
using Poco::SharedPtr; using Poco::SharedPtr;
namespace ServerConfig { namespace ServerConfig {
#define SESSION_TIMEOUT_DEFAULT 10
Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX]; Mnemonic g_Mnemonic_WordLists[MNEMONIC_MAX];
ObfusArray* g_ServerCryptoKey = nullptr; ObfusArray* g_ServerCryptoKey = nullptr;
// std::string g_ServerAdminPublic; // std::string g_ServerAdminPublic;
UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr; UniLib::controller::CPUSheduler* g_CPUScheduler = nullptr;
Context::Ptr g_SSL_CLient_Context = nullptr; Context::Ptr g_SSL_CLient_Context = nullptr;
EmailAccount g_EmailAccount; EmailAccount g_EmailAccount;
int g_SessionTimeout = SESSION_TIMEOUT_DEFAULT;
bool loadMnemonicWordLists() bool loadMnemonicWordLists()
{ {
@ -66,6 +70,8 @@ namespace ServerConfig {
g_ServerCryptoKey = new ObfusArray(realBinSize, key); g_ServerCryptoKey = new ObfusArray(realBinSize, key);
//g_ServerAdminPublic = cfg.getString("crypto.server_admin_public"); //g_ServerAdminPublic = cfg.getString("crypto.server_admin_public");
g_SessionTimeout = cfg.getInt("session.timeout", SESSION_TIMEOUT_DEFAULT);
return true; return true;
} }

View File

@ -27,6 +27,7 @@ namespace ServerConfig {
extern UniLib::controller::CPUSheduler* g_CPUScheduler; extern UniLib::controller::CPUSheduler* g_CPUScheduler;
extern Poco::Net::Context::Ptr g_SSL_CLient_Context; extern Poco::Net::Context::Ptr g_SSL_CLient_Context;
extern EmailAccount g_EmailAccount; extern EmailAccount g_EmailAccount;
extern int g_SessionTimeout;
bool loadMnemonicWordLists(); bool loadMnemonicWordLists();
bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg); bool initServerCrypto(const Poco::Util::LayeredConfiguration& cfg);

View File

@ -1,4 +1,6 @@
#include "SessionManager.h" #include "SessionManager.h"
#include "ErrorManager.h"
#include "../ServerConfig.h"
#include <sodium.h> #include <sodium.h>
@ -33,10 +35,10 @@ bool SessionManager::init()
case VALIDATE_EMAIL: mValidations[i] = new Poco::RegularExpression("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"); break; case VALIDATE_EMAIL: mValidations[i] = new Poco::RegularExpression("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"); break;
case VALIDATE_PASSWORD: mValidations[i] = new Poco::RegularExpression("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&+-])[A-Za-z0-9@$!%*?&+-]{8,}$"); break; case VALIDATE_PASSWORD: mValidations[i] = new Poco::RegularExpression("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&+-])[A-Za-z0-9@$!%*?&+-]{8,}$"); break;
case VALIDATE_PASSPHRASE: mValidations[i] = new Poco::RegularExpression("^(?:[a-z]* ){23}[a-z]*\s*$"); break; case VALIDATE_PASSPHRASE: mValidations[i] = new Poco::RegularExpression("^(?:[a-z]* ){23}[a-z]*\s*$"); break;
case VALIDATE_HAS_NUMBER: mValidations[i] = new Poco::RegularExpression("[0-9]"); break; case VALIDATE_HAS_NUMBER: mValidations[i] = new Poco::RegularExpression(".*[0-9].*"); break;
case VALIDATE_HAS_SPECIAL_CHARACTER: mValidations[i] = new Poco::RegularExpression("[@$!%*?&+-]"); break; case VALIDATE_HAS_SPECIAL_CHARACTER: mValidations[i] = new Poco::RegularExpression(".*[@$!%*?&+-].*"); break;
case VALIDATE_HAS_UPPERCASE_LETTER: mValidations[i] = new Poco::RegularExpression("[A-Z]"); break; case VALIDATE_HAS_UPPERCASE_LETTER: mValidations[i] = new Poco::RegularExpression(".*[A-Z].*"); break;
case VALIDATE_HAS_LOWERCASE_LETTER: mValidations[i] = new Poco::RegularExpression("[a-z]"); break; case VALIDATE_HAS_LOWERCASE_LETTER: mValidations[i] = new Poco::RegularExpression(".*[a-z].*"); break;
default: printf("[SessionManager::%s] unknown validation type\n", __FUNCTION__); default: printf("[SessionManager::%s] unknown validation type\n", __FUNCTION__);
} }
} }
@ -77,6 +79,25 @@ bool SessionManager::isValid(const std::string& subject, SessionValidationTypes
return *mValidations[validationType] == subject; return *mValidations[validationType] == subject;
} }
int SessionManager::generateNewUnusedHandle()
{
int newHandle = 0;
int maxTrys = 0;
do {
newHandle = randombytes_random();
maxTrys++;
} while (mRequestSessionMap.find(newHandle) != mRequestSessionMap.end() && maxTrys < 100);
if (maxTrys >= 100 || 0 == newHandle) {
auto em = ErrorManager::getInstance();
em->addError(new ParamError("SessionManager::generateNewUnusedHandle", "can't find new handle, have already ", std::to_string(mRequestSessionMap.size())));
em->sendErrorsAsEmail();
//printf("[SessionManager::%s] can't find new handle, have already: %d",
//__FUNCTION__, mRequestSessionMap.size());
return 0;
}
return newHandle;
}
Session* SessionManager::getNewSession(int* handle) Session* SessionManager::getNewSession(int* handle)
{ {
@ -84,15 +105,22 @@ Session* SessionManager::getNewSession(int* handle)
printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__); printf("[SessionManager::%s] not initialized any more\n", __FUNCTION__);
return nullptr; return nullptr;
} }
// first check if we have any timeouted session to directly reuse it
checkTimeoutSession();
// lock // lock
mWorkingMutex.lock(); mWorkingMutex.lock();
//UniLib::controller::TaskPtr checkSessionTimeout(new CheckSessionTimeouted);
//checkSessionTimeout->scheduleTask(checkSessionTimeout);
// check if we have an existing session ready to use // check if we have an existing session ready to use
if (mEmptyRequestStack.size() > 0) { while (mEmptyRequestStack.size() > 0) {
int local_handle = mEmptyRequestStack.top(); int local_handle = mEmptyRequestStack.top();
mEmptyRequestStack.pop(); mEmptyRequestStack.pop();
auto resultIt = mRequestSessionMap.find(local_handle); auto resultIt = mRequestSessionMap.find(local_handle);
if (resultIt != mRequestSessionMap.end()) { if (resultIt != mRequestSessionMap.end() && !resultIt->second->isActive()) {
Session* result = resultIt->second; Session* result = resultIt->second;
result->reset(); result->reset();
mWorkingMutex.unlock(); mWorkingMutex.unlock();
@ -100,38 +128,34 @@ Session* SessionManager::getNewSession(int* handle)
if (handle) { if (handle) {
*handle = local_handle; *handle = local_handle;
} }
result->setActive(true);
return result; return result;
} }
} }
else {
// else create new RequestSession Object // else create new RequestSession Object
// calculate random handle // calculate random handle
// check if already exist, if get new // check if already exist, if get new
int newHandle = 0; int newHandle = generateNewUnusedHandle();
int maxTrys = 0; if (!newHandle) {
do {
newHandle = randombytes_random();
maxTrys++;
} while (mRequestSessionMap.find(newHandle) != mRequestSessionMap.end() && maxTrys < 100);
if (maxTrys >= 100 || 0 == newHandle) {
printf("[SessionManager::%s] can't find new handle, have already: %d",
__FUNCTION__, mRequestSessionMap.size());
return nullptr;
}
auto requestSession = new Session(newHandle);
mRequestSessionMap.insert(std::pair<int, Session*>(newHandle, requestSession));
mWorkingMutex.unlock(); mWorkingMutex.unlock();
return nullptr;
if (handle) {
*handle = newHandle;
}
return requestSession;
} }
return nullptr; auto requestSession = new Session(newHandle);
mRequestSessionMap.insert(std::pair<int, Session*>(newHandle, requestSession));
requestSession->setActive(true);
mWorkingMutex.unlock();
if (handle) {
*handle = newHandle;
}
return requestSession;
//return nullptr;
} }
bool SessionManager::releseSession(int requestHandleSession) bool SessionManager::releseSession(int requestHandleSession)
@ -147,8 +171,25 @@ bool SessionManager::releseSession(int requestHandleSession)
mWorkingMutex.unlock(); mWorkingMutex.unlock();
return false; return false;
} }
it->second->reset(); Session* session = it->second;
mEmptyRequestStack.push(requestHandleSession); session->reset();
session->setActive(false);
// change request handle we don't want session hijacking
int newHandle = generateNewUnusedHandle();
// erase after generating new number to prevent to getting the same number again
mRequestSessionMap.erase(requestHandleSession);
if (!newHandle) {
delete session;
mWorkingMutex.unlock();
return true;
}
session->setHandle(newHandle);
mRequestSessionMap.insert(std::pair<int, Session*>(newHandle, session));
mEmptyRequestStack.push(newHandle);
mWorkingMutex.unlock(); mWorkingMutex.unlock();
return true; return true;
@ -181,6 +222,8 @@ Session* SessionManager::getSession(int handle)
auto it = mRequestSessionMap.find(handle); auto it = mRequestSessionMap.find(handle);
if (it != mRequestSessionMap.end()) { if (it != mRequestSessionMap.end()) {
result = it->second; result = it->second;
result->setActive(true);
result->updateTimeout();
} }
mWorkingMutex.unlock(); mWorkingMutex.unlock();
return result; return result;
@ -188,11 +231,47 @@ Session* SessionManager::getSession(int handle)
Session* SessionManager::findByEmailVerificationCode(long long emailVerificationCode) Session* SessionManager::findByEmailVerificationCode(long long emailVerificationCode)
{ {
Session* result = nullptr;
mWorkingMutex.lock();
for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) { for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) {
if (it->second->getEmailVerificationCode() == emailVerificationCode) { if (it->second->getEmailVerificationCode() == emailVerificationCode) {
return it->second; result = it->second;
break;
} }
} }
mWorkingMutex.unlock();
return nullptr; return result;
}
void SessionManager::checkTimeoutSession()
{
mWorkingMutex.lock();
auto now = Poco::DateTime();
// random variance within 10 seconds for timeout to make it harder to get information and hack the server
auto timeout = Poco::Timespan(ServerConfig::g_SessionTimeout * 60, randombytes_random() % 10000000);
std::stack<int> toRemove;
for (auto it = mRequestSessionMap.begin(); it != mRequestSessionMap.end(); it++) {
if (!it->second->isActive()) continue;
Poco::Timespan timeElapsed(now - it->second->getLastActivity());
if (timeElapsed > timeout) {
toRemove.push(it->first);
}
}
mWorkingMutex.unlock();
while (toRemove.size() > 0) {
int handle = toRemove.top();
toRemove.pop();
releseSession(handle);
}
}
int CheckSessionTimeouted::run()
{
SessionManager::getInstance()->checkTimeoutSession();
return 0;
} }

View File

@ -46,6 +46,7 @@ public:
} }
bool releseSession(int requestHandleSession); bool releseSession(int requestHandleSession);
bool isExist(int requestHandleSession); bool isExist(int requestHandleSession);
// try to find existing session, return nullptr if not found
Session* getSession(int handle); Session* getSession(int handle);
Session* findByEmailVerificationCode(long long emailVerificationCode); Session* findByEmailVerificationCode(long long emailVerificationCode);
@ -54,9 +55,12 @@ public:
bool isValid(const std::string& subject, SessionValidationTypes validationType); bool isValid(const std::string& subject, SessionValidationTypes validationType);
void checkTimeoutSession();
protected: protected:
SessionManager(); SessionManager();
int generateNewUnusedHandle();
// access mutex // access mutex
std::mutex mWorkingMutex; std::mutex mWorkingMutex;
@ -71,4 +75,11 @@ protected:
Poco::RegularExpression* mValidations[VALIDATE_MAX]; Poco::RegularExpression* mValidations[VALIDATE_MAX];
}; };
class CheckSessionTimeouted : public UniLib::controller::CPUTask
{
public:
virtual int run();
virtual const char* getResourceType() const { return "CheckSessionTimeouted"; };
};
#endif //DR_LUA_WEB_MODULE_SESSION_MANAGER_H #endif //DR_LUA_WEB_MODULE_SESSION_MANAGER_H

View File

@ -20,7 +20,7 @@ int WriteEmailVerification::run()
{ {
Profiler timeUsed; Profiler timeUsed;
auto verificationCode = mSession->getEmailVerificationCode(); auto verificationCode = mSession->getEmailVerificationCode();
printf("{[WriteEmailVerification::run] E-Mail Verification Code: %llu\n", verificationCode); //printf("[WriteEmailVerification::run] E-Mail Verification Code: %llu\n", verificationCode);
auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); auto dbSession = ConnectionManager::getInstance()->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
int user_id = mUser->getDBId(); int user_id = mUser->getDBId();
Poco::Data::Statement insert(dbSession); Poco::Data::Statement insert(dbSession);
@ -30,7 +30,7 @@ int WriteEmailVerification::run()
mSession->addError(new Error("WriteEmailVerification", "error inserting email verification code")); mSession->addError(new Error("WriteEmailVerification", "error inserting email verification code"));
return -1; return -1;
} }
printf("[WriteEmailVerification::run] timeUsed: %s\n", timeUsed.string().data()); printf("[WriteEmailVerification] timeUsed: %s\n", timeUsed.string().data());
return 0; return 0;
} }
@ -63,7 +63,7 @@ int WritePassphraseIntoDB::run()
em->sendErrorsAsEmail(); em->sendErrorsAsEmail();
} }
printf("[WritePassphraseIntoDB::run] timeUsed: %s\n", timeUsed.string().data()); printf("[WritePassphraseIntoDB] timeUsed: %s\n", timeUsed.string().data());
return 0; return 0;
} }
@ -71,7 +71,7 @@ int WritePassphraseIntoDB::run()
// -------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------
Session::Session(int handle) Session::Session(int handle)
: mHandleId(handle), mSessionUser(nullptr), mEmailVerificationCode(0), mState(SESSION_STATE_EMPTY) : mHandleId(handle), mSessionUser(nullptr), mEmailVerificationCode(0), mState(SESSION_STATE_EMPTY), mActive(false)
{ {
} }
@ -90,7 +90,10 @@ void Session::reset()
mSessionUser = nullptr; mSessionUser = nullptr;
} }
updateTimeout(); updateTimeout();
updateState(SESSION_STATE_EMPTY);
mPassphrase = "";
mClientLoginIP = Poco::Net::IPAddress(); mClientLoginIP = Poco::Net::IPAddress();
mEmailVerificationCode = 0;
} }
void Session::updateTimeout() void Session::updateTimeout()
@ -111,6 +114,8 @@ bool Session::createUser(const std::string& name, const std::string& email, cons
return false; return false;
} }
if (!sm->isValid(password, VALIDATE_PASSWORD)) { if (!sm->isValid(password, VALIDATE_PASSWORD)) {
addError(new Error("Passwort", "Bitte gebe ein g&uuml;ltiges Password ein mit mindestens 8 Zeichen, Gro&szlig;- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen ein!"));
// @$!%*?&+- // @$!%*?&+-
if (password.size() < 8) { if (password.size() < 8) {
addError(new Error("Passwort", "Dein Passwort ist zu kurz!")); addError(new Error("Passwort", "Dein Passwort ist zu kurz!"));
@ -127,7 +132,7 @@ bool Session::createUser(const std::string& name, const std::string& email, cons
else if (!sm->isValid(password, VALIDATE_HAS_SPECIAL_CHARACTER)) { else if (!sm->isValid(password, VALIDATE_HAS_SPECIAL_CHARACTER)) {
addError(new Error("Passwort", "Dein Passwort enth&auml;lt keine Sonderzeichen (@$!%*?&+-)!")); addError(new Error("Passwort", "Dein Passwort enth&auml;lt keine Sonderzeichen (@$!%*?&+-)!"));
} }
addError(new Error("Passwort", "Bitte gebe ein g&uuml;ltiges Password ein mit mindestens 8 Zeichen, Gro&szlig;- und Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen"));
return false; return false;
} }
/*if (passphrase.size() > 0 && !sm->isValid(passphrase, VALIDATE_PASSPHRASE)) { /*if (passphrase.size() > 0 && !sm->isValid(passphrase, VALIDATE_PASSPHRASE)) {
@ -215,6 +220,7 @@ bool Session::createUser(const std::string& name, const std::string& email, cons
bool Session::updateEmailVerification(unsigned long long emailVerificationCode) bool Session::updateEmailVerification(unsigned long long emailVerificationCode)
{ {
Profiler usedTime; Profiler usedTime;
const static char* funcName = "Session::updateEmailVerification"; const static char* funcName = "Session::updateEmailVerification";
auto em = ErrorManager::getInstance(); auto em = ErrorManager::getInstance();
@ -243,6 +249,7 @@ bool Session::updateEmailVerification(unsigned long long emailVerificationCode)
} }
updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED); updateState(SESSION_STATE_EMAIL_VERIFICATION_CODE_CHECKED);
printf("[%s] time: %s\n", funcName, usedTime.string().data()); printf("[%s] time: %s\n", funcName, usedTime.string().data());
return true; return true;
} }
else { else {
@ -347,8 +354,12 @@ void Session::detectSessionState()
Poco::Net::HTTPCookie Session::getLoginCookie() Poco::Net::HTTPCookie Session::getLoginCookie()
{ {
auto keks = Poco::Net::HTTPCookie("user", std::to_string(mHandleId)); auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId));
// TODO: additional config, like js permit // prevent reading or changing cookie with js
keks.setHttpOnly();
// send cookie only via https
keks.setSecure(true);
return keks; return keks;
} }
@ -407,7 +418,8 @@ bool Session::loadFromEmailVerificationCode(unsigned long long emailVerification
void Session::updateState(SessionStates newState) void Session::updateState(SessionStates newState)
{ {
lock(); lock();
printf("[Session::%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState)); updateTimeout();
printf("[%s] newState: %s\n", __FUNCTION__, translateSessionStateToString(newState));
if (newState > mState) { if (newState > mState) {
mState = newState; mState = newState;
} }

View File

@ -39,11 +39,12 @@ enum SessionStates {
SESSION_STATE_COUNT SESSION_STATE_COUNT
}; };
class SessionManager;
class Session : public ErrorList, public UniLib::lib::MultithreadContainer class Session : public ErrorList, public UniLib::lib::MultithreadContainer
{ {
friend WriteEmailVerification; friend WriteEmailVerification;
friend SessionManager;
public: public:
Session(int handle); Session(int handle);
~Session(); ~Session();
@ -63,6 +64,7 @@ public:
inline User* getUser() { return mSessionUser; } inline User* getUser() { return mSessionUser; }
inline int getHandle() { return mHandleId; } inline int getHandle() { return mHandleId; }
inline void setPassphrase(const std::string& passphrase) { mPassphrase = passphrase; } inline void setPassphrase(const std::string& passphrase) { mPassphrase = passphrase; }
inline const std::string& getPassphrase() { return mPassphrase; } inline const std::string& getPassphrase() { return mPassphrase; }
bool generatePassphrase(); bool generatePassphrase();
@ -81,14 +83,21 @@ public:
inline unsigned long long getEmailVerificationCode() { return mEmailVerificationCode; } inline unsigned long long getEmailVerificationCode() { return mEmailVerificationCode; }
inline bool isActive() const { return mActive; }
inline void setActive(bool active) { mActive = active; }
inline Poco::DateTime getLastActivity() { return mLastActivity; }
protected: protected:
void updateTimeout(); void updateTimeout();
inline void setHandle(int newHandle) { mHandleId = newHandle; }
void createEmailVerificationCode(); void createEmailVerificationCode();
void detectSessionState(); void detectSessionState();
static const char* translateSessionStateToString(SessionStates state); static const char* translateSessionStateToString(SessionStates state);
private:
int mHandleId; int mHandleId;
User* mSessionUser; User* mSessionUser;
std::string mPassphrase; std::string mPassphrase;
@ -97,6 +106,8 @@ protected:
unsigned long long mEmailVerificationCode; unsigned long long mEmailVerificationCode;
SessionStates mState; SessionStates mState;
bool mActive;
}; };

View File

@ -0,0 +1,57 @@
#include "PrepareEmailTask.h"
#include "../model/Profiler.h"
#include "../ServerConfig.h"
#include "../SingletonManager/ErrorManager.h"
PrepareEmailTask::PrepareEmailTask(UniLib::controller::CPUSheduler* cpuScheduler)
: UniLib::controller::CPUTask(cpuScheduler), mMailClientSession(nullptr)
{
}
PrepareEmailTask::~PrepareEmailTask()
{
if (mMailClientSession) {
delete mMailClientSession;
}
}
int PrepareEmailTask::run()
{
Profiler timeUsed;
mMailClientSession = new Poco::Net::SecureSMTPClientSession(ServerConfig::g_EmailAccount.url, ServerConfig::g_EmailAccount.port);
mMailClientSession->login();
mMailClientSession->startTLS(ServerConfig::g_SSL_CLient_Context);
mMailClientSession->login(Poco::Net::SMTPClientSession::AUTH_LOGIN, ServerConfig::g_EmailAccount.username, ServerConfig::g_EmailAccount.password);
printf("[PrepareEmailTask] time: %s\n", timeUsed.string().data());
/*
session.login();
session.startTLS(pContext);
if (!username.empty())
{
session.login(SMTPClientSession::AUTH_LOGIN, username, password);
}
session.sendMessage(message);
session.close();
*/
return 0;
}
int PrepareEmailTask::send(Poco::Net::MailMessage* message)
{
auto er = ErrorManager::getInstance();
try {
mMailClientSession->sendMessage(*message);
mMailClientSession->close();
}
catch (Poco::Exception& exc) {
er->addError(new ParamError("PrepareEmailTask::send", "error sending email", exc.displayText().data()));
printf("[PrepareEmailTask::%s] error sending email: %s\n", __FUNCTION__, exc.displayText().data());
return -1;
}
return 0;
}

View File

@ -0,0 +1,23 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE
#define GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE
#include "CPUTask.h"
#include "Poco/Net/SecureSMTPClientSession.h"
class PrepareEmailTask : public UniLib::controller::CPUTask
{
public:
PrepareEmailTask(UniLib::controller::CPUSheduler* cpuScheduler);
virtual ~PrepareEmailTask();
virtual int run();
int send(Poco::Net::MailMessage* message);
virtual const char* getResourceType() const { return "PrepareEmailTask"; };
protected:
private:
Poco::Net::SecureSMTPClientSession* mMailClientSession;
};
#endif //GRADIDO_LOGIN_SERVER_TASKS_PREPAIRE_EMAIL_TASK_INCLUDE

View File

@ -1,5 +1,6 @@
#include "SendEmailTask.h" #include "SendEmailTask.h"
#include "PrepareEmailTask.h" #include "PrepareEmailTask.h"
#include "../model/Profiler.h"
#include "../SingletonManager/ErrorManager.h" #include "../SingletonManager/ErrorManager.h"
#include "../ServerConfig.h" #include "../ServerConfig.h"
@ -19,6 +20,7 @@ SendEmailTask::~SendEmailTask()
int SendEmailTask::run() int SendEmailTask::run()
{ {
Profiler timeUsed;
auto er = ErrorManager::getInstance(); auto er = ErrorManager::getInstance();
auto parent = getParent(0); auto parent = getParent(0);
@ -31,5 +33,6 @@ int SendEmailTask::run()
if (prepare->send(mMailMessage)) { if (prepare->send(mMailMessage)) {
return -1; return -1;
} }
printf("[SendEmailTask] time: %s\n", timeUsed.string().data());
return 0; return 0;
} }

View File

@ -21,6 +21,12 @@
response.redirect(uri_start + "/"); response.redirect(uri_start + "/");
return; return;
} }
} else {
// remove old cookies if exist
auto keks = Poco::Net::HTTPCookie("GRADIDO_LOGIN", std::to_string(mHandleId))
// max age of 0 delete cookie
keks.setMaxAge(0);
response.addCookie(keks);
} }
%> %>

View File

@ -17,10 +17,7 @@ enum PageState
bool hasErrors = mSession->errorCount() > 0; bool hasErrors = mSession->errorCount() > 0;
// save login cookie, because maybe we've get an new session // save login cookie, because maybe we've get an new session
auto cookie_id = mSession->getHandle(); response.addCookie(mSession->getLoginCookie());
auto user_host = request.clientAddress().host();
mSession->setClientIp(user_host);
response.addCookie(Poco::Net::HTTPCookie("user", std::to_string(cookie_id)));
if (!form.empty()) { if (!form.empty()) {
auto registerKeyChoice = form.get("passphrase", ""); auto registerKeyChoice = form.get("passphrase", "");
@ -90,7 +87,7 @@ label:not(.grd_radio_label) {
<a href="/saveKeys">Weiter</a> <a href="/saveKeys">Weiter</a>
</div> </div>
<% } else if(state == PAGE_ASK_PASSPHRASE) { %> <% } else if(state == PAGE_ASK_PASSPHRASE) { %>
<form method="POST"> <form method="POST" action="/passphrase">
<fieldset class="grd_container_small"> <fieldset class="grd_container_small">
<legend>Neue Gradido Adresse anlegen / wiederherstellen</legend> <legend>Neue Gradido Adresse anlegen / wiederherstellen</legend>
<p>Hast du schonmal ein Gradido Konto besessen?</p> <p>Hast du schonmal ein Gradido Konto besessen?</p>