Bruteforce Mitigation
This commit is contained in:
@ -64,6 +64,13 @@ sudo -u postgres psql -c "CREATE TABLE passwordResetKeys(
|
||||
);
|
||||
" cavecomm
|
||||
|
||||
sudo -u postgres psql -c "CREATE TABLE loginLockOut(
|
||||
email text PRIMARY KEY,
|
||||
attempts int,
|
||||
expiration timestamp
|
||||
);
|
||||
" cavecomm
|
||||
|
||||
sudo -u postgres psql -c "CREATE TABLE aliasRoutes(
|
||||
aliasName text PRIMARY KEY,
|
||||
freelancerID int,
|
||||
|
10
spec/spec.md
10
spec/spec.md
@ -250,11 +250,13 @@ optional:{default}
|
||||
emailServerPort={587}
|
||||
emailAddressDisplay={Cavecomm Automated Management System}
|
||||
regularTaskExecution={false}
|
||||
regularTaskExecutionIntervalSeconds={900} 15min
|
||||
regularTaskExecutionIntervalSeconds={900} 15min
|
||||
regularTaskExecutionModules={}
|
||||
options separated by "|":
|
||||
reeeprint: test module
|
||||
counter: test module
|
||||
|
||||
bruteForceMitigationCleaner: Removes expired entries from the "loginLockOut"
|
||||
freelancerResetKeyCleaner: Removes expired entries from the "passwordResetKeys"
|
||||
itemsPerPage={0} 0 == no pagination
|
||||
bruteForceMitigationLockSeconds={900} 15min How long a login lock out is valid
|
||||
bruteForceMitigationAttempts={5} How many login attempts can be made until a login lock out is set
|
||||
|
||||
|
||||
|
@ -222,7 +222,6 @@ namespace Database {
|
||||
/*
|
||||
* Executes the PURGE_EXPIRED_FREELANCER_RESET_KEYS statement
|
||||
* Takes an open pqxx::connection
|
||||
* todo::regular execution
|
||||
*/
|
||||
void executeStatement_STATEMENT_PURGE_EXPIRED_FREELANCER_RESET_KEYS(pqxx::connection &connection) {
|
||||
pqxx::work work(connection);
|
||||
@ -230,6 +229,16 @@ namespace Database {
|
||||
work.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the PURGE_EXPIRED_LOGIN_LOCKOUTS statement
|
||||
* Takes an open pqxx::connection
|
||||
*/
|
||||
void executeStatement_STATEMENT_PURGE_EXPIRED_LOGIN_LOCKOUTS(pqxx::connection &connection) {
|
||||
pqxx::work work(connection);
|
||||
work.exec(SQL_STATEMENT_PURGE_EXPIRED_LOGIN_LOCKOUTS);
|
||||
work.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement SELECT_ITEM_BY_ID
|
||||
* Takes an open pqxx::connection and the id to select by
|
||||
@ -332,6 +341,73 @@ namespace Database {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement SELECT_CHECK_LOGIN_LOCK_OUT
|
||||
* Takes an open pqxx::connection and the emailAddress
|
||||
* Delivers true if the login for the given email is locked
|
||||
*/
|
||||
pqxx::result executePreparedStatement_SELECT_CHECK_LOGIN_LOCK_OUT(pqxx::connection &connection, const std::string& emailAddress) {
|
||||
pqxx::work work(connection);
|
||||
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT, emailAddress);
|
||||
work.commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement SELECT_GET_LOGIN_LOCK_OUT_MINUTES
|
||||
* Takes an open pqxx::connection and the emailAddress
|
||||
* Delivers minutes until the login lock out expires
|
||||
*/
|
||||
pqxx::result executePreparedStatement_SELECT_GET_LOGIN_LOCK_OUT_MINUTES(pqxx::connection &connection, const std::string& emailAddress) {
|
||||
pqxx::work work(connection);
|
||||
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_SELECT_GET_LOGIN_LOCK_OUT_MINUTES, emailAddress);
|
||||
work.commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement INSERT_LOGIN_LOCK_OUT
|
||||
* Takes an open pqxx::connection and the emailAddress
|
||||
*/
|
||||
void executePreparedStatement_INSERT_LOGIN_LOCK_OUT(pqxx::connection &connection, const std::string& emailAddress) {
|
||||
pqxx::work work(connection);
|
||||
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_INSERT_LOGIN_LOCK_OUT, emailAddress);
|
||||
work.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS
|
||||
* Takes an open pqxx::connection and the emailAddress
|
||||
*/
|
||||
void executePreparedStatement_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS(pqxx::connection &connection, const std::string& emailAddress) {
|
||||
pqxx::work work(connection);
|
||||
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS, emailAddress);
|
||||
work.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement CHECK_LOGIN_LOCK_OUT_ATTEMPTS
|
||||
* Takes an open pqxx::connection the emailAddress and the max attempts
|
||||
* returns true if the lock out attempts, exceed or equate the given max attempts
|
||||
*/
|
||||
pqxx::result executePreparedStatement_CHECK_LOGIN_LOCK_OUT_ATTEMPTS(pqxx::connection &connection, const std::string& emailAddress, int maxAttempts) {
|
||||
pqxx::work work(connection);
|
||||
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS, emailAddress, maxAttempts);
|
||||
work.commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement UPDATE_EXPIRATION_LOGIN_LOCK_OUT
|
||||
* Takes an open pqxx::connection the emailAddress and the additive lock out in seconds
|
||||
*/
|
||||
void executePreparedStatement_UPDATE_EXPIRATION_LOGIN_LOCK_OUT(pqxx::connection &connection, const std::string& emailAddress, int lockOutSeconds) {
|
||||
pqxx::work work(connection);
|
||||
std::string lockOutString = std::to_string(lockOutSeconds) + " second";
|
||||
pqxx::result result = work.exec_prepared(PREPARED_STATEMENT_UPDATE_EXPIRATION_LOGIN_LOCK_OUT, emailAddress, lockOutSeconds);
|
||||
work.commit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes the prepared statement SELECT_TEMPLATE
|
||||
* Takes an open pqxx::connection and the id to select by
|
||||
|
@ -150,6 +150,42 @@ namespace DatabaseStatementConstCollection {
|
||||
const static std::string PREPARED_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET = "selectFreelancersWithCommissionstateLimitedAndOffset";
|
||||
const static std::string SQL_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET = "select id, name, basicInformation , (case when (commissionlimit <= (select count(*) as requestCount from requests where requests.accepted = true and requests.completed = false and requests.freelancerid = freelancers.id group by freelancers.id)) then 'Closed' else 'Open' end) as commissionsclosed from freelancers order by name limit $1 offset $2;";
|
||||
|
||||
/*
|
||||
* Name and Statement for prepared statement to check the email login lock out
|
||||
*/
|
||||
const static std::string PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT = "selectCheckLoginLockOut";
|
||||
const static std::string SQL_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT = "select (COALESCE(expiration, CURRENT_TIMESTAMP) - CURRENT_TIMESTAMP) > INTERVAL '0 second' from loginlockout where email = $1;";
|
||||
|
||||
/*
|
||||
* Name and Statement for prepared statement to get the minutes till expiration of an email login lock out
|
||||
*/
|
||||
const static std::string PREPARED_STATEMENT_SELECT_GET_LOGIN_LOCK_OUT_MINUTES = "selectGetLoginLockOutMinutes";
|
||||
const static std::string SQL_STATEMENT_SELECT_GET_LOGIN_LOCK_OUT_MINUTES = "select to_char((COALESCE(expiration, CURRENT_TIMESTAMP) - CURRENT_TIMESTAMP), 'MI') from loginlockout where email = $1;";
|
||||
|
||||
/*
|
||||
* Name and Statement for prepared statement to increment the login attempts for an email
|
||||
*/
|
||||
const static std::string PREPARED_STATEMENT_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS = "updateIncrementLoginLockOutAttempts";
|
||||
const static std::string SQL_STATEMENT_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS = "update loginlockout set attempts = attempts + 1 where email = $1;";
|
||||
|
||||
/*
|
||||
* Name and Statement for prepared statement to try to add a new entry into the login lockout
|
||||
*/
|
||||
const static std::string PREPARED_STATEMENT_INSERT_LOGIN_LOCK_OUT = "updateIncrementLoginLockOutAttempts";
|
||||
const static std::string SQL_STATEMENT_INSERT_LOGIN_LOCK_OUT = "insert into loginlockout values ($1, 0, CURRENT_TIMESTAMP) on conflict do nothing;";
|
||||
|
||||
/*
|
||||
* Name and Statement for prepared statement to try to check if a login lock out has exceeded the max attempts
|
||||
*/
|
||||
const static std::string PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS = "selectCheckLoginLockOutAttempts";
|
||||
const static std::string SQL_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS = "select count(*) = 1 from loginlockout where email = $1 and attempts >= $2;";
|
||||
|
||||
/*
|
||||
* Name and Statement for prepared statement to update the expiration and reset the login attempts
|
||||
*/
|
||||
const static std::string PREPARED_STATEMENT_UPDATE_EXPIRATION_LOGIN_LOCK_OUT = "updateExpirationLoginLockOut";
|
||||
const static std::string SQL_STATEMENT_UPDATE_EXPIRATION_LOGIN_LOCK_OUT = "update loginlockout set (attempts, expiration) = (0, CURRENT_TIMESTAMP + INTERVAL $2 ) where email = $1;";
|
||||
|
||||
/*
|
||||
* IDs of prepared statements
|
||||
*/
|
||||
@ -176,11 +212,17 @@ namespace DatabaseStatementConstCollection {
|
||||
const static int ID_INSERT_FREELANCER_RESET_KEY = 20;
|
||||
const static int ID_DELETE_FREELANCER_RESET_KEY = 21;
|
||||
const static int ID_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET = 22;
|
||||
const static int ID_SELECT_CHECK_LOGIN_LOCK_OUT = 23;
|
||||
const static int ID_SELECT_GET_LOGIN_LOCK_OUT_MINUTES = 24;
|
||||
const static int ID_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS = 25;
|
||||
const static int ID_INSERT_LOGIN_LOCK_OUT = 26;
|
||||
const static int ID_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS = 27;
|
||||
const static int ID_UPDATE_EXPIRATION_LOGIN_LOCK_OUT = 28;
|
||||
|
||||
/*
|
||||
* Easy access to prepared statements via prepared statement name
|
||||
*/
|
||||
const static std::map<std::string, std::string> preparedStatementCollection = {
|
||||
const static std::unordered_map<std::string, std::string> preparedStatementCollection = {
|
||||
{PREPARED_STATEMENT_INSERT_ITEM_IN_REQUESTS, SQL_STATEMENT_INSERT_ITEM_IN_REQUESTS},
|
||||
{PREPARED_STATEMENT_INSERT_NEW_FREELANCER, SQL_STATEMENT_INSERT_NEW_FREELANCER},
|
||||
{PREPARED_STATEMENT_SELECT_ITEM_BY_ID, SQL_STATEMENT_SELECT_ITEM_BY_ID},
|
||||
@ -203,12 +245,18 @@ namespace DatabaseStatementConstCollection {
|
||||
{PREPARED_STATEMENT_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED, SQL_STATEMENT_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED},
|
||||
{PREPARED_STATEMENT_INSERT_FREELANCER_RESET_KEY, SQL_STATEMENT_INSERT_FREELANCER_RESET_KEY},
|
||||
{PREPARED_STATEMENT_DELETE_FREELANCER_RESET_KEY, SQL_STATEMENT_DELETE_FREELANCER_RESET_KEY},
|
||||
{PREPARED_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET, SQL_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET}
|
||||
{PREPARED_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET, SQL_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET},
|
||||
{PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT, SQL_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT},
|
||||
{PREPARED_STATEMENT_SELECT_GET_LOGIN_LOCK_OUT_MINUTES, SQL_STATEMENT_SELECT_GET_LOGIN_LOCK_OUT_MINUTES},
|
||||
{PREPARED_STATEMENT_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS, SQL_STATEMENT_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS},
|
||||
{PREPARED_STATEMENT_INSERT_LOGIN_LOCK_OUT, SQL_STATEMENT_INSERT_LOGIN_LOCK_OUT},
|
||||
{PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS, SQL_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS},
|
||||
{PREPARED_STATEMENT_UPDATE_EXPIRATION_LOGIN_LOCK_OUT, SQL_STATEMENT_UPDATE_EXPIRATION_LOGIN_LOCK_OUT}
|
||||
};
|
||||
/*
|
||||
* Easy access to prepared statement name via int
|
||||
*/
|
||||
const static std::map<int, std::string> preparedStatementNameCollection = {
|
||||
const static std::unordered_map<int, std::string> preparedStatementNameCollection = {
|
||||
{ID_INSERT_ITEM_IN_REQUESTS, PREPARED_STATEMENT_INSERT_ITEM_IN_REQUESTS},
|
||||
{ID_INSERT_NEW_FREELANCER, PREPARED_STATEMENT_INSERT_NEW_FREELANCER},
|
||||
{ID_SELECT_ITEM_BY_ID, PREPARED_STATEMENT_SELECT_ITEM_BY_ID},
|
||||
@ -231,7 +279,13 @@ namespace DatabaseStatementConstCollection {
|
||||
{ID_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED, PREPARED_STATEMENT_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED},
|
||||
{ID_INSERT_FREELANCER_RESET_KEY, PREPARED_STATEMENT_INSERT_FREELANCER_RESET_KEY},
|
||||
{ID_DELETE_FREELANCER_RESET_KEY, PREPARED_STATEMENT_DELETE_FREELANCER_RESET_KEY},
|
||||
{ID_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET, PREPARED_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET}
|
||||
{ID_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET, PREPARED_STATEMENT_SELECT_FREELANCERS_WITHCOMMISSIONSSTATE_LIMITED_AND_OFFSET},
|
||||
{ID_SELECT_CHECK_LOGIN_LOCK_OUT, PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT},
|
||||
{ID_SELECT_GET_LOGIN_LOCK_OUT_MINUTES, PREPARED_STATEMENT_SELECT_GET_LOGIN_LOCK_OUT_MINUTES},
|
||||
{ID_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS, PREPARED_STATEMENT_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS},
|
||||
{ID_INSERT_LOGIN_LOCK_OUT, PREPARED_STATEMENT_INSERT_LOGIN_LOCK_OUT},
|
||||
{ID_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS, PREPARED_STATEMENT_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS},
|
||||
{ID_UPDATE_EXPIRATION_LOGIN_LOCK_OUT, PREPARED_STATEMENT_UPDATE_EXPIRATION_LOGIN_LOCK_OUT}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -239,6 +293,11 @@ namespace DatabaseStatementConstCollection {
|
||||
*/
|
||||
const static std::string SQL_STATEMENT_PURGE_EXPIRED_FREELANCER_RESET_KEYS = "delete from passwordresetkeys where expiration <= CURRENT_TIMESTAMP;";
|
||||
|
||||
/*
|
||||
* Statement to remove expired login lockouts
|
||||
*/
|
||||
const static std::string SQL_STATEMENT_PURGE_EXPIRED_LOGIN_LOCKOUTS = "delete from loginlockout where expiration <= CURRENT_TIMESTAMP;";
|
||||
|
||||
/*
|
||||
* Selects freelancers, their basicInfo and if the commissions are closed/open ordered by name.
|
||||
* Delivers if the commission limit has been reached and if the commissions are closed based on the accepted/completed state of the freelancers requests
|
||||
@ -249,6 +308,5 @@ namespace DatabaseStatementConstCollection {
|
||||
* Selects freelancer count
|
||||
*/
|
||||
const static std::string SQL_Statement_SELECT_FREELANCERS_COUNT = "select count(*) from freelancers;";
|
||||
|
||||
}
|
||||
#endif
|
@ -12,11 +12,12 @@
|
||||
# Optional: {default}
|
||||
# emailServerPort={587}
|
||||
# emailAddressDisplay={Cavecomm Automated Management System}
|
||||
# regularTaskExecution={false}
|
||||
# regularTaskExecution={true}
|
||||
# regularTaskExecutionIntervalSeconds={900} 15min
|
||||
# regularTaskExecutionModules={} # options separated by "|": reeeprint|counter
|
||||
# itemsPerPage{0} #0 == no pagination
|
||||
|
||||
# regularTaskExecutionModules={} # options separated by "|": bruteForceMitigationCleaner|freelancerResetKeyCleaner
|
||||
# itemsPerPage={0} #0 == no pagination
|
||||
# bruteForceMitigationLockSeconds={900} 15min
|
||||
# bruteForceMitigationAttempts={5}
|
||||
|
||||
|
||||
|
||||
@ -40,10 +41,15 @@ domain=192.168.56.1:18080;
|
||||
|
||||
regularTaskExecution=true;
|
||||
|
||||
regularTaskExecutionIntervalSeconds=2;
|
||||
regularTaskExecutionIntervalSeconds=900;
|
||||
|
||||
regularTaskExecutionModules=;
|
||||
regularTaskExecutionModules=bruteForceMitigationCleaner|freelancerResetKeyCleaner;
|
||||
|
||||
itemsPerPage=5;
|
||||
|
||||
bruteForceMitigationLockSeconds=900;
|
||||
|
||||
bruteForceMitigationAttempts=5;
|
||||
|
||||
|
||||
#configend#
|
||||
|
55
src/main.cpp
55
src/main.cpp
@ -338,6 +338,7 @@ int main(int argc, char *argv[]) {
|
||||
ID_DELETE_FREELANCER_RESET_KEY,
|
||||
ID_INSERT_FREELANCER_RESET_KEY
|
||||
});
|
||||
|
||||
pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email);
|
||||
int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str());
|
||||
if (checkFreelancerExistsExtracted == 1) {
|
||||
@ -472,26 +473,38 @@ int main(int argc, char *argv[]) {
|
||||
ID_SELECT_CHECK_EMAIL_EXISTS,
|
||||
ID_SELECT_FREELANCER_SALT,
|
||||
ID_SELECT_CHECK_HASH_VALID,
|
||||
ID_UPDATE_LOGIN_VALIDATION_KEY
|
||||
ID_UPDATE_LOGIN_VALIDATION_KEY,
|
||||
ID_SELECT_CHECK_LOGIN_LOCK_OUT,
|
||||
ID_SELECT_GET_LOGIN_LOCK_OUT_MINUTES
|
||||
});
|
||||
pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email);
|
||||
int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str());
|
||||
if (checkFreelancerExistsExtracted == 1) {
|
||||
pqxx::result freelancerSalt = Database::executePreparedStatement_SELECT_FREELANCER_SALT(databaseConnection, email);
|
||||
string salt = freelancerSalt.at(0).at(0).c_str();
|
||||
string hash = Utilities::hashPassword(salt, password);
|
||||
pqxx::result checkFreelancerHash = Database::executePreparedStatement_SELECT_CHECK_HASH_VALID(databaseConnection, email, hash);
|
||||
int checkFreelancerHashExtracted = stoi(checkFreelancerHash.at(0).at(0).c_str());
|
||||
if (checkFreelancerHashExtracted == 1) {
|
||||
//create secureCookie
|
||||
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
|
||||
std::string loginKeyValue = Utilities::generateRandomHashValueSHA512();
|
||||
Database::executePreparedStatement_UPDATE_LOGIN_VALIDATION_KEY(databaseConnection, loginKeyValue, email);
|
||||
std::string loginKeyCookieValue = Utilities::generateSecureCookieLoginKeyValue(loginKeyValue, stayLoggedIn);
|
||||
std::string freelancerEmailCookieValue = Utilities::generateSecureCookieFreelancerEmailValue(email, stayLoggedIn);
|
||||
cookieCtx.set_cookie("loginKey", loginKeyCookieValue);
|
||||
cookieCtx.set_cookie("freelancerEmail",freelancerEmailCookieValue);
|
||||
ctx[MUSTACHE_LOGIN_SUCCESS] = true;
|
||||
|
||||
pqxx::result checkloginLockedOut = Database::executePreparedStatement_SELECT_CHECK_LOGIN_LOCK_OUT(databaseConnection, email);
|
||||
string checkloginLockedOutExtracted = checkloginLockedOut.at(0).at(0).c_str();
|
||||
if (checkloginLockedOutExtracted != "true") {
|
||||
pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email);
|
||||
int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str());
|
||||
if (checkFreelancerExistsExtracted == 1) {
|
||||
pqxx::result freelancerSalt = Database::executePreparedStatement_SELECT_FREELANCER_SALT(databaseConnection, email);
|
||||
string salt = freelancerSalt.at(0).at(0).c_str();
|
||||
string hash = Utilities::hashPassword(salt, password);
|
||||
pqxx::result checkFreelancerHash = Database::executePreparedStatement_SELECT_CHECK_HASH_VALID(databaseConnection, email, hash);
|
||||
int checkFreelancerHashExtracted = stoi(checkFreelancerHash.at(0).at(0).c_str());
|
||||
if (checkFreelancerHashExtracted == 1) {
|
||||
//create secureCookie
|
||||
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
|
||||
std::string loginKeyValue = Utilities::generateRandomHashValueSHA512();
|
||||
Database::executePreparedStatement_UPDATE_LOGIN_VALIDATION_KEY(databaseConnection, loginKeyValue, email);
|
||||
std::string loginKeyCookieValue = Utilities::generateSecureCookieLoginKeyValue(loginKeyValue, stayLoggedIn);
|
||||
std::string freelancerEmailCookieValue = Utilities::generateSecureCookieFreelancerEmailValue(email, stayLoggedIn);
|
||||
cookieCtx.set_cookie("loginKey", loginKeyCookieValue);
|
||||
cookieCtx.set_cookie("freelancerEmail",freelancerEmailCookieValue);
|
||||
ctx[MUSTACHE_LOGIN_SUCCESS] = true;
|
||||
}
|
||||
else {
|
||||
ctx[MUSTACHE_LOGIN_ERROR] = true;
|
||||
ctx[MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID] = true;
|
||||
Utilities::loginLockOutIncrement(configuration, databaseConnection, email);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx[MUSTACHE_LOGIN_ERROR] = true;
|
||||
@ -499,8 +512,10 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
pqxx::result loginLockOutInMinutes = Database::executePreparedStatement_SELECT_GET_LOGIN_LOCK_OUT_MINUTES(databaseConnection, email);
|
||||
ctx[MUSTACHE_LOGIN_ERROR] = true;
|
||||
ctx[MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID] = true;
|
||||
ctx[MUSTACHE_LOGIN_ERROR_LOCKED_OUT] = true;
|
||||
ctx[MUSTACHE_LOGIN_ERROR_LOCKED_OUT_MINUTES] = loginLockOutInMinutes.at(0).at(0).c_str();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -14,31 +14,31 @@ using namespace std::chrono_literals;
|
||||
*/
|
||||
namespace RegularTaskExecution {
|
||||
/*
|
||||
* print test
|
||||
* todo::remove
|
||||
* clears expired login lockouts
|
||||
*/
|
||||
void reeePrint() {
|
||||
std::cout << "reeee" << std::endl;
|
||||
void purgeExpiredLoginLockouts(pqxx::connection &connection) {
|
||||
Database::executeStatement_STATEMENT_PURGE_EXPIRED_LOGIN_LOCKOUTS(connection);
|
||||
}
|
||||
|
||||
/*
|
||||
* print test
|
||||
* todo::remove
|
||||
* clears expired Freelancer Reset Keys
|
||||
*/
|
||||
void counterPrint(size_t& counter) {
|
||||
std::cout << counter << std::endl;
|
||||
counter++;
|
||||
void purgeExpiredFreelancerResetKeys(pqxx::connection &connection) {
|
||||
Database::executeStatement_STATEMENT_PURGE_EXPIRED_FREELANCER_RESET_KEYS(connection);
|
||||
}
|
||||
|
||||
void regularExecution(const Utilities::config& configuration) {
|
||||
std::chrono::seconds seconds(configuration.regularTaskExecutionIntervalSeconds);
|
||||
pqxx::connection databaseConnection(configuration.databaseConnectionString);
|
||||
size_t counter = 0;
|
||||
std::cout << "REGULAR EXECUTION START: " << std::endl;
|
||||
while (configuration.regularTaskExecution) {
|
||||
if (configuration.regularTaskExecutionModules.at(Utilities::MODULE_NAME_REEE_PRINT))
|
||||
reeePrint(); // print test todo::remove
|
||||
if (configuration.regularTaskExecutionModules.at(Utilities::MODULE_NAME_REEE_PRINT))
|
||||
counterPrint(counter); // print test todo::remove
|
||||
|
||||
std::cout << "REGULAR EXECUTION LOOP: " << counter << std::endl;
|
||||
if (configuration.regularTaskExecutionModules.at(Utilities::MODULE_NAME_BRUTE_FORCE_MITIGATION_CLEANER))
|
||||
purgeExpiredLoginLockouts(databaseConnection);
|
||||
if (configuration.regularTaskExecutionModules.at(Utilities::MODULE_NAME_FREELANCER_RESET_KEY_CLEANER))
|
||||
purgeExpiredFreelancerResetKeys(databaseConnection);
|
||||
counter++;
|
||||
std::this_thread::sleep_for(seconds);
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ namespace TemplateConstCollection {
|
||||
const static std::string MUSTACHE_LOGIN_ERROR = "LOGIN_ERROR";
|
||||
const static std::string MUSTACHE_LOGIN_ERROR_EMAIL_PASS_NOT_FILLED = "LOGIN_ERROR_EMAIL_PASS_NOT_FILLED";
|
||||
const static std::string MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID = "LOGIN_ERROR_LOGIN_DATA_INVALID";
|
||||
const static std::string MUSTACHE_LOGIN_ERROR_LOCKED_OUT = "LOGIN_ERROR_LOCKED_OUT";
|
||||
const static std::string MUSTACHE_LOGIN_ERROR_LOCKED_OUT_MINUTES = "LOGIN_ERROR_LOCKED_OUT_MINUTES";
|
||||
const static std::string MUSTACHE_PASSWORD_EMPTY = "PASSWORD_EMPTY";
|
||||
const static std::string MUSTACHE_PASSWORD_RESET_DOES_NOT_EXIST = "PASSWORD_RESET_DOES_NOT_EXIST";
|
||||
const static std::string MUSTACHE_PASSWORD_RESET_EXPIRED = "PASSWORD_RESET_EXPIRED";
|
||||
@ -58,5 +60,6 @@ namespace TemplateConstCollection {
|
||||
const static std::string MUSTACHE_PAGINATION_NUMBERS = "PAGINATION_NUMBERS";
|
||||
const static std::string MUSTACHE_PAGINATION_PREVIOUS = "PAGINATION_PREVIOUS";
|
||||
const static std::string MUSTACHE_PAGINATION_NEXT = "PAGINATION_NEXT";
|
||||
|
||||
}
|
||||
#endif
|
@ -11,7 +11,7 @@
|
||||
#include <stdexcept>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
#include <pqxx/pqxx>
|
||||
@ -31,12 +31,12 @@ using namespace TemplateConstCollection;
|
||||
* Utility Manager
|
||||
*/
|
||||
namespace Utilities {
|
||||
const static std::map<std::string, std::string> HTML_URL_CODES = {{"%20", " "}, {"%21", "!"}, {"%22", "\""}, {"%23", "#"}, {"%24", "$"}, {"%25", "%"}, {"%26", "&"}, {"%27", "'"}, {"%28", "("}, {"%29", ")"}, {"%2A", "*"}, {"%2B", "+"}, {"%2C", ","}, {"%2D", "-"}, {"%2E", "."}, {"%2F", "/"}, {"%30", "0"}, {"%31", "1"}, {"%32", "2"}, {"%33", "3"}, {"%34", "4"}, {"%35", "5"}, {"%36", "6"}, {"%37", "7"}, {"%38", "8"}, {"%39", "9"}, {"%3A", ":"}, {"%3B", ";"}, {"%3C", "<"}, {"%3D", "="}, {"%3E", ">"}, {"%3F", "?"}, {"%40", "@"}, {"%5B", "["}, {"%5C", "\\"}, {"%5D", "]"}, {"%5E", "^"}, {"%5F", "_"}, {"%60", "`"}, {"%7B", "{"}, {"%7C", "|"}, {"%7D", "}"}, {"%7E", "~"}, {"%7F", " "}, {"%80", "€"}, {"%82", "‚"}, {"%83", "ƒ"}, {"%84", "„"}, {"%85", "…"}, {"%86", "†"}, {"%87", "‡"}, {"%88", "ˆ"}, {"%89", "‰"}, {"%8A", "Š"}, {"%8B", "‹"}, {"%8C", "Œ"}, {"%8E", "Ž"}, {"%91", "‘"}, {"%92", "’"}, {"%93", "“"}, {"%94", "”"}, {"%95", "•"}, {"%96", "–"}, {"%97", "—"}, {"%98", "˜"}, {"%99", "™"}, {"%9A", "š"}, {"%9B", "›"}, {"%9C", "œ"}, {"%9E", "ž"}, {"%9F", "Ÿ"}, {"%A1", "¡"}, {"%A2", "¢"}, {"%A3", "£"}, {"%A4", "¤"}, {"%A5", "¥"}, {"%A6", "¦"}, {"%A7", "§"}, {"%A8", "¨"}, {"%A9", "©"}, {"%AA", "ª"}, {"%AB", "«"}, {"%AC", "¬"}, {"%AE", "®"}, {"%AF", "¯"}, {"%B0", "°"}, {"%B1", "±"}, {"%B2", "²"}, {"%B3", "³"}, {"%B4", "´"}, {"%B5", "µ"}, {"%B6", "¶"}, {"%B7", "·"}, {"%B8", "¸"}, {"%B9", "¹"}, {"%BA", "º"}, {"%BB", "»"}, {"%BC", "¼"}, {"%BD", "½"}, {"%BE", "¾"}, {"%BF", "¿"}, {"%C0", "À"}, {"%C1", "Á"}, {"%C2", "Â"}, {"%C3", "Ã"}, {"%C4", "Ä"}, {"%C5", "Å"}, {"%C6", "Æ"}, {"%C7", "Ç"}, {"%C8", "È"}, {"%C9", "É"}, {"%CA", "Ê"}, {"%CB", "Ë"}, {"%CC", "Ì"}, {"%CD", "Í"}, {"%CE", "Î"}, {"%CF", "Ï"}, {"%D0", "Ð"}, {"%D1", "Ñ"}, {"%D2", "Ò"}, {"%D3", "Ó"}, {"%D4", "Ô"}, {"%D5", "Õ"}, {"%D6", "Ö"}, {"%D7", "×"}, {"%D8", "Ø"}, {"%D9", "Ù"}, {"%DA", "Ú"}, {"%DB", "Û"}, {"%DC", "Ü"}, {"%DD", "Ý"}, {"%DE", "Þ"}, {"%DF", "ß"}, {"%E0", "à"}, {"%E1", "á"}, {"%E2", "â"}, {"%E3", "ã"}, {"%E4", "ä"}, {"%E5", "å"}, {"%E6", "æ"}, {"%E7", "ç"}, {"%E8", "è"}, {"%E9", "é"}, {"%EA", "ê"}, {"%EB", "ë"}, {"%EC", "ì"}, {"%ED", "í"}, {"%EE", "î"}, {"%EF", "ï"}, {"%F0", "ð"}, {"%F1", "ñ"}, {"%F2", "ò"}, {"%F3", "ó"}, {"%F4", "ô"}, {"%F5", "õ"}, {"%F6", "ö"}, {"%F7", "÷"}, {"%F8", "ø"}, {"%F9", "ù"}, {"%FA", "ú"}, {"%FB", "û"}, {"%FC", "ü"}, {"%FD", "ý"}, {"%FE", "þ"}, {"%FF", "ÿ"}};
|
||||
const static std::unordered_map<std::string, std::string> HTML_URL_CODES = {{"%20", " "}, {"%21", "!"}, {"%22", "\""}, {"%23", "#"}, {"%24", "$"}, {"%25", "%"}, {"%26", "&"}, {"%27", "'"}, {"%28", "("}, {"%29", ")"}, {"%2A", "*"}, {"%2B", "+"}, {"%2C", ","}, {"%2D", "-"}, {"%2E", "."}, {"%2F", "/"}, {"%30", "0"}, {"%31", "1"}, {"%32", "2"}, {"%33", "3"}, {"%34", "4"}, {"%35", "5"}, {"%36", "6"}, {"%37", "7"}, {"%38", "8"}, {"%39", "9"}, {"%3A", ":"}, {"%3B", ";"}, {"%3C", "<"}, {"%3D", "="}, {"%3E", ">"}, {"%3F", "?"}, {"%40", "@"}, {"%5B", "["}, {"%5C", "\\"}, {"%5D", "]"}, {"%5E", "^"}, {"%5F", "_"}, {"%60", "`"}, {"%7B", "{"}, {"%7C", "|"}, {"%7D", "}"}, {"%7E", "~"}, {"%7F", " "}, {"%80", "€"}, {"%82", "‚"}, {"%83", "ƒ"}, {"%84", "„"}, {"%85", "…"}, {"%86", "†"}, {"%87", "‡"}, {"%88", "ˆ"}, {"%89", "‰"}, {"%8A", "Š"}, {"%8B", "‹"}, {"%8C", "Œ"}, {"%8E", "Ž"}, {"%91", "‘"}, {"%92", "’"}, {"%93", "“"}, {"%94", "”"}, {"%95", "•"}, {"%96", "–"}, {"%97", "—"}, {"%98", "˜"}, {"%99", "™"}, {"%9A", "š"}, {"%9B", "›"}, {"%9C", "œ"}, {"%9E", "ž"}, {"%9F", "Ÿ"}, {"%A1", "¡"}, {"%A2", "¢"}, {"%A3", "£"}, {"%A4", "¤"}, {"%A5", "¥"}, {"%A6", "¦"}, {"%A7", "§"}, {"%A8", "¨"}, {"%A9", "©"}, {"%AA", "ª"}, {"%AB", "«"}, {"%AC", "¬"}, {"%AE", "®"}, {"%AF", "¯"}, {"%B0", "°"}, {"%B1", "±"}, {"%B2", "²"}, {"%B3", "³"}, {"%B4", "´"}, {"%B5", "µ"}, {"%B6", "¶"}, {"%B7", "·"}, {"%B8", "¸"}, {"%B9", "¹"}, {"%BA", "º"}, {"%BB", "»"}, {"%BC", "¼"}, {"%BD", "½"}, {"%BE", "¾"}, {"%BF", "¿"}, {"%C0", "À"}, {"%C1", "Á"}, {"%C2", "Â"}, {"%C3", "Ã"}, {"%C4", "Ä"}, {"%C5", "Å"}, {"%C6", "Æ"}, {"%C7", "Ç"}, {"%C8", "È"}, {"%C9", "É"}, {"%CA", "Ê"}, {"%CB", "Ë"}, {"%CC", "Ì"}, {"%CD", "Í"}, {"%CE", "Î"}, {"%CF", "Ï"}, {"%D0", "Ð"}, {"%D1", "Ñ"}, {"%D2", "Ò"}, {"%D3", "Ó"}, {"%D4", "Ô"}, {"%D5", "Õ"}, {"%D6", "Ö"}, {"%D7", "×"}, {"%D8", "Ø"}, {"%D9", "Ù"}, {"%DA", "Ú"}, {"%DB", "Û"}, {"%DC", "Ü"}, {"%DD", "Ý"}, {"%DE", "Þ"}, {"%DF", "ß"}, {"%E0", "à"}, {"%E1", "á"}, {"%E2", "â"}, {"%E3", "ã"}, {"%E4", "ä"}, {"%E5", "å"}, {"%E6", "æ"}, {"%E7", "ç"}, {"%E8", "è"}, {"%E9", "é"}, {"%EA", "ê"}, {"%EB", "ë"}, {"%EC", "ì"}, {"%ED", "í"}, {"%EE", "î"}, {"%EF", "ï"}, {"%F0", "ð"}, {"%F1", "ñ"}, {"%F2", "ò"}, {"%F3", "ó"}, {"%F4", "ô"}, {"%F5", "õ"}, {"%F6", "ö"}, {"%F7", "÷"}, {"%F8", "ø"}, {"%F9", "ù"}, {"%FA", "ú"}, {"%FB", "û"}, {"%FC", "ü"}, {"%FD", "ý"}, {"%FE", "þ"}, {"%FF", "ÿ"}};
|
||||
const static int SALT_SIZE = 32;
|
||||
const static std::string SALT_CHAR_SET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
const static std::string MODULE_NAME_REEE_PRINT = "reeeprint";
|
||||
const static std::string MODULE_NAME_COUNTER_PRINT = "counter";
|
||||
const static std::string MODULE_NAME_BRUTE_FORCE_MITIGATION_CLEANER = "bruteForceMitigationCleaner";
|
||||
const static std::string MODULE_NAME_FREELANCER_RESET_KEY_CLEANER = "freelancerResetKeyCleaner";
|
||||
|
||||
/*
|
||||
* Takes string to split it into a vector based on a given delimiter
|
||||
@ -73,13 +73,15 @@ namespace Utilities {
|
||||
std::string sslCrtPath;
|
||||
std::string sslKeyPath;
|
||||
std::string domain;
|
||||
bool regularTaskExecution = false;
|
||||
bool regularTaskExecution = true;
|
||||
int regularTaskExecutionIntervalSeconds = 900;
|
||||
std::map<std::string, bool> regularTaskExecutionModules = {
|
||||
{MODULE_NAME_REEE_PRINT, false},
|
||||
{MODULE_NAME_COUNTER_PRINT, false}
|
||||
std::unordered_map<std::string, bool> regularTaskExecutionModules = {
|
||||
{MODULE_NAME_BRUTE_FORCE_MITIGATION_CLEANER, false},
|
||||
{MODULE_NAME_FREELANCER_RESET_KEY_CLEANER, false}
|
||||
};
|
||||
int itemsPerPage = 20;
|
||||
int bruteForceMitigationLockSeconds = 900;
|
||||
int bruteForceMitigationAttempts = 5;
|
||||
|
||||
/*
|
||||
* validates existence of mandatory variables in config
|
||||
@ -202,6 +204,14 @@ namespace Utilities {
|
||||
itemsPerPage = std::stoi(lineString);
|
||||
continue;
|
||||
}
|
||||
if (lineVector.at(0) == "bruteForceMitigationLockSeconds") {
|
||||
bruteForceMitigationLockSeconds = std::stoi(lineString);
|
||||
continue;
|
||||
}
|
||||
if (lineVector.at(0) == "bruteForceMitigationAttempts") {
|
||||
bruteForceMitigationAttempts = std::stoi(lineString);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -474,5 +484,24 @@ namespace Utilities {
|
||||
|
||||
return loginValid;
|
||||
}
|
||||
|
||||
/*
|
||||
* increments the login lock out attempts and if they have reached the set limit set expiration
|
||||
*/
|
||||
void loginLockOutIncrement(const Utilities::config& configuration, pqxx::connection &connection, const std::string& emailAddress) {
|
||||
Database::prepareStatements(connection, {
|
||||
ID_INSERT_LOGIN_LOCK_OUT,
|
||||
ID_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS,
|
||||
ID_SELECT_CHECK_LOGIN_LOCK_OUT_ATTEMPTS,
|
||||
ID_UPDATE_EXPIRATION_LOGIN_LOCK_OUT
|
||||
});
|
||||
Database::executePreparedStatement_INSERT_LOGIN_LOCK_OUT(connection, emailAddress);
|
||||
Database::executePreparedStatement_UPDATE_INCREMENT_LOGIN_LOCK_OUT_ATTEMPTS(connection, emailAddress);
|
||||
pqxx::result loginAttemptsCheck = Database::executePreparedStatement_CHECK_LOGIN_LOCK_OUT_ATTEMPTS(connection, emailAddress, configuration.bruteForceMitigationAttempts);
|
||||
std::string loginAttemptsCheckExtracted = loginAttemptsCheck.at(0).at(0).c_str();
|
||||
if (loginAttemptsCheckExtracted == "true") {
|
||||
Database::executePreparedStatement_UPDATE_EXPIRATION_LOGIN_LOCK_OUT(connection, emailAddress, configuration.bruteForceMitigationLockSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -10,6 +10,11 @@
|
||||
</div>
|
||||
{{/LOGIN_SUCCESS}}
|
||||
{{#LOGIN_ERROR}}
|
||||
{{#LOGIN_ERROR_LOCKED_OUT}}
|
||||
<div>
|
||||
Login failed too often please try again in {{LOGIN_ERROR_LOCKED_OUT_MINUTES}}
|
||||
</div>
|
||||
{{/LOGIN_ERROR_LOCKED_OUT}}
|
||||
{{#LOGIN_ERROR_LOGIN_DATA_INVALID}}
|
||||
<div>
|
||||
Login failed login data invalid
|
||||
|
Reference in New Issue
Block a user