CVE-2012-5618 (PHP, published 2020-02-04T14:15Z)
Description: value: Ushahidi before 2.6.1 has insufficient entropy for forgot-password tokens.
Severity: MEDIUM (CVSS v3 base score: 9.8)
Weakness: CWE-640 \u2014 Weak Password Recovery Mechanism for Forgotten Password
The product contains a mechanism for users to recover or change their passwords without knowing the original password, but the mechanism is weak.
Repository: https://github.com/ushahidi/Ushahidi_Web
Files changed:
application/controllers/login.php
application/hooks/2_settings.php
application/models/user.php
Commit message: Make forgot password tokens use better random token #646

Fixes security issue discovered by Timothy D. Morgan <tim.morgan@owasp.org>
Forgotten password challenges were guessable based on users last login
and email address. Tokens are now generated based on a HMAC of login time
and email address using a salt and secret key specifically for these
tokens.


Vulnerable code:

						// Secret consists of email and the last_login field.
						// So as soon as the user logs in again,
						// the reset link expires automatically.
						$secret = $auth->hash_password($user->email.$user->last_login);
						$secret_link = url::site('login/index/'.$user->id.'/'.$secret.'?reset');
						$email_sent = $this->_email_resetlink($post->resetemail,$user->name,$secret_link);

				if($auth->hash_password($user->email.$user->last_login, $auth->find_salt($token)) == $token)

Fixed code:
						$secret = $user->forgot_password_token();
						$secret_link = url::site('login/index/'.$user->id.'/'.urlencode($secret).'?reset');
						$email_sent = $this->_email_resetlink($post->resetemail, $user->name, $secret_link);
				if($user->check_forgot_password_token($token))

// Set 'settings.forgot_password_key' if not set already
if ( ! Kohana::config('settings.forgot_password_secret'))
{
	$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+[]{};:,.?`~';
	$key = text::random($pool, 64);
	Settings_Model::save_setting('forgot_password_secret', $key);
	Kohana::config_set('settings.forgot_password_secret', $key);
}
	
	/**
	 * Get a new forgotten password challenge token for this user
	 * @param string $salt Optional salt for token generation (use this)
	 * @return string
	 */
	public function forgot_password_token()
	{
		return $this->_forgot_password_token();
	}

	/**
	 * Check to see if forgotten password token is valid
	 * @param string $token token to check
	 * @return boolean is token valid
	 **/
	public function check_forgot_password_token($token)
	{
		$salt = substr($token, 0, 32);
		return $this->_forgot_password_token($salt) == $token;
	}

	/**
	 * Generate a forgotten password challenge token for this user
	 * @param string $salt Optional salt for token generation (only use this for checking a token in URL)
	 * @return string token
	 */
	private function _forgot_password_token($salt = FALSE)
	{
		// Secret consists of email and the last_login field.
		// So as soon as the user logs in again, the reset link expires automatically.
		$salt = $salt ? $salt : text::random('alnum', 32); // Limited charset to keep it URL friendly
		$key = Kohana::config('settings.forgot_password_secret');
		return $salt . hash_hmac('sha1', $this->last_login . $this->email, $salt . $key);
	}