Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Action throttle #116

Closed
wants to merge 6 commits into from

3 participants

@teamwebgalli

Added a general throttle system which is used by
1) Password reset request 2) Login

@cash
Owner

Please open a ticket for this at http://trac.elgg.org/. We prefer discussing ideas there.

@ewinslow
Owner

I'm closing this for now to clean out our PR queue. Let's definitely continue discussion on the linked ticket.

@ewinslow ewinslow closed this
@sembrestels sembrestels referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@sembrestels sembrestels referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 14, 2011
  1. @teamwebgalli

    Moved common functions like log_action_failure(),reset_action_failure…

    teamwebgalli authored
    …_count(),check_action_rate_limit_exceeded()
  2. @teamwebgalli
  3. @teamwebgalli
  4. @teamwebgalli
Commits on Dec 17, 2011
  1. @teamwebgalli

    Added a password request reset to login, in case the user remembers t…

    teamwebgalli authored
    …he password after requesting a reset.
Commits on Jan 23, 2012
  1. @teamwebgalli
This page is out of date. Refresh to see the latest.
View
116 engine/lib/actions.php
@@ -333,6 +333,122 @@ function generate_action_token($timestamp) {
}
/**
+ * Log a failed action for $user_guid
+ *
+ * @param int $user_guid User GUID
+ *
+ * @param $action is the particular action. Can be a login attempt, password reset reqeust or anything
+ *
+ * @return bool
+ */
+function log_action_failure($user_guid,$action) {
+ $user_guid = (int)$user_guid;
+ $user = get_entity($user_guid);
+
+ if (!$action){
+ return false;
+ }
+
+ if (($user_guid) && ($user) && ($user instanceof ElggUser)) {
+ $fails = (int)$user->getPrivateSetting($action);
+ $fails++;
+
+ $user->setPrivateSetting($action, $fails);
+ $user->setPrivateSetting("$action_$fails", time());
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Resets the fail action count for $user_guid
+ *
+ * @param int $user_guid User GUID
+ *
+ * @param $action is the particular action. Can be a login attempt, password reset reqeust or anything
+ *
+ * @return bool true on success (success = user has no logged failed attempts)
+ */
+function reset_action_failure_count($user_guid,$action) {
+ $user_guid = (int)$user_guid;
+ $user = get_entity($user_guid);
+
+ if (!$action){
+ return false;
+ }
+
+ if (($user_guid) && ($user) && ($user instanceof ElggUser)) {
+ $fails = (int)$user->getPrivateSetting($action);
+
+ if ($fails) {
+ for ($n = 1; $n <= $fails; $n++) {
+ $user->removePrivateSetting("$action_$n");
+ }
+
+ $user->removePrivateSetting($action);
+
+ return true;
+ }
+
+ // nothing to reset
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Checks if the rate limit of a failed action has been exceeded for $user_guid.
+ *
+ * @param int $user_guid User GUID
+ * @param $action is the particular action. Can be a login attempt, password reset reqeust or anything
+ * @param int $limit is the maximum number of times an action is allowed to executed
+ * @param int $time is the the time interval in seconds.
+ * @return bool on exceeded limit.
+ */
+function check_action_rate_limit_exceeded($user_guid, $action, $limit , $interval) {
+ // 5 failures in 5 minutes causes temporary block on the particular action
+ $limit = (int)$limit;
+ if (!$limit){
+ $limit = 5;
+ }
+
+ $interval = (int)$interval;
+ if(!$interval){
+ $interval = 5*60;
+ }
+
+ $user_guid = (int)$user_guid;
+ $user = get_entity($user_guid);
+
+ if (!$action){
+ return false;
+ }
+
+ if (($user_guid) && ($user) && ($user instanceof ElggUser)) {
+ $fails = (int)$user->getPrivateSetting($action);
+ if ($fails >= $limit) {
+ $cnt = 0;
+ $time = time();
+ for ($n = $fails; $n > 0; $n--) {
+ $f = $user->getPrivateSetting("$action_$n");
+ if ($f > ($time - $interval)) {
+ $cnt++;
+ }
+
+ if ($cnt == $limit) {
+ // Limit reached
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
* Initialise the site secret hash.
*
* Used during installation and saves as a datalist.
View
95 engine/lib/sessions.php
@@ -168,106 +168,18 @@ function pam_auth_userpass(array $credentials = array()) {
throw new LoginException(elgg_echo('LoginException:UsernameFailure'));
}
- if (check_rate_limit_exceeded($user->guid)) {
+ if (check_action_rate_limit_exceeded($user->guid, 'login_failure', 5 , 300)) {
throw new LoginException(elgg_echo('LoginException:AccountLocked'));
}
if ($user->password !== generate_user_password($user, $credentials['password'])) {
- log_login_failure($user->guid);
+ log_action_failure($user->guid,'login_failure');
throw new LoginException(elgg_echo('LoginException:PasswordFailure'));
}
return true;
}
-/**
- * Log a failed login for $user_guid
- *
- * @param int $user_guid User GUID
- *
- * @return bool
- */
-function log_login_failure($user_guid) {
- $user_guid = (int)$user_guid;
- $user = get_entity($user_guid);
-
- if (($user_guid) && ($user) && ($user instanceof ElggUser)) {
- $fails = (int)$user->getPrivateSetting("login_failures");
- $fails++;
-
- $user->setPrivateSetting("login_failures", $fails);
- $user->setPrivateSetting("login_failure_$fails", time());
- return true;
- }
-
- return false;
-}
-
-/**
- * Resets the fail login count for $user_guid
- *
- * @param int $user_guid User GUID
- *
- * @return bool true on success (success = user has no logged failed attempts)
- */
-function reset_login_failure_count($user_guid) {
- $user_guid = (int)$user_guid;
- $user = get_entity($user_guid);
-
- if (($user_guid) && ($user) && ($user instanceof ElggUser)) {
- $fails = (int)$user->getPrivateSetting("login_failures");
-
- if ($fails) {
- for ($n = 1; $n <= $fails; $n++) {
- $user->removePrivateSetting("login_failure_$n");
- }
-
- $user->removePrivateSetting("login_failures");
-
- return true;
- }
-
- // nothing to reset
- return true;
- }
-
- return false;
-}
-
-/**
- * Checks if the rate limit of failed logins has been exceeded for $user_guid.
- *
- * @param int $user_guid User GUID
- *
- * @return bool on exceeded limit.
- */
-function check_rate_limit_exceeded($user_guid) {
- // 5 failures in 5 minutes causes temporary block on logins
- $limit = 5;
- $user_guid = (int)$user_guid;
- $user = get_entity($user_guid);
-
- if (($user_guid) && ($user) && ($user instanceof ElggUser)) {
- $fails = (int)$user->getPrivateSetting("login_failures");
- if ($fails >= $limit) {
- $cnt = 0;
- $time = time();
- for ($n = $fails; $n > 0; $n--) {
- $f = $user->getPrivateSetting("login_failure_$n");
- if ($f > $time - (60 * 5)) {
- $cnt++;
- }
-
- if ($cnt == $limit) {
- // Limit reached
- return true;
- }
- }
- }
- }
-
- return false;
-}
/**
* Logs in a specified ElggUser. For standard registration, use in conjunction
@@ -319,7 +231,8 @@ function login(ElggUser $user, $persistent = false) {
// Update statistics
set_last_login($_SESSION['guid']);
- reset_login_failure_count($user->guid); // Reset any previous failed login attempts
+ reset_action_failure_count($user->guid, 'login_failure'); // Reset any previous failed login attempts
+ reset_action_failure_count($user->guid, 'password_reset'); // Reset any ppassword reset requests
return true;
}
View
14 engine/lib/users.php
@@ -678,7 +678,11 @@ function send_new_password_request($user_guid) {
// generate code
$code = generate_random_cleartext_password();
$user->setPrivateSetting('passwd_conf_code', $code);
-
+
+ //check whether the total request exceeded a fixed value
+ if (check_action_rate_limit_exceeded($user_guid, 'password_reset', 3 , 86400)) {
+ return false;
+ }
// generate link
$link = $CONFIG->site->url . "resetpassword?u=$user_guid&c=$code";
@@ -686,6 +690,9 @@ function send_new_password_request($user_guid) {
// generate email
$email = elgg_echo('email:resetreq:body', array($user->name, $_SERVER['REMOTE_ADDR'], $link));
+ // log the mails sent after each mail
+ log_action_failure($user_guid,'password_reset');
+
return notify_user($user->guid, $CONFIG->site->guid,
elgg_echo('email:resetreq:subject'), $email, NULL, 'email');
}
@@ -744,8 +751,9 @@ function execute_new_password_request($user_guid, $conf_code) {
if (force_user_password_reset($user_guid, $password)) {
remove_private_setting($user_guid, 'passwd_conf_code');
- // clean the logins failures
- reset_login_failure_count($user_guid);
+ // clean the logins failures and the passwordreset count
+ reset_action_failure_count($user_guid,'login_failures');
+ reset_action_failure_count($user_guid,'password_reset');
$email = elgg_echo('email:resetpassword:body', array($user->name, $password));
View
2  mod/notifications/manifest.xml
@@ -2,7 +2,7 @@
<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
<name>Notifications</name>
<author>Core developers</author>
- <version>1.7</version>
+ <version>1.8</version>
<category>bundled</category>
<description>Elgg notifications plugin</description>
<website>http://www.elgg.org/</website>
View
4 mod/pages/pages/pages/history.php
@@ -9,12 +9,12 @@
$page = get_entity($page_guid);
if (!$page) {
-
+ forward('');
}
$container = $page->getContainerEntity();
if (!$container) {
-
+ forward('');
}
elgg_set_page_owner_guid($container->getGUID());
Something went wrong with that request. Please try again.