diff options
Diffstat (limited to 'engine/lib/actions.php')
| -rw-r--r-- | engine/lib/actions.php | 117 | 
1 files changed, 88 insertions, 29 deletions
diff --git a/engine/lib/actions.php b/engine/lib/actions.php index 53b185dea..8047914ac 100644 --- a/engine/lib/actions.php +++ b/engine/lib/actions.php @@ -65,18 +65,16 @@ function action($action, $forwarder = "") {  	// @todo REMOVE THESE ONCE #1509 IS IN PLACE.  	// Allow users to disable plugins without a token in order to  	// remove plugins that are incompatible. -	// Login and logout are for convenience. +	// Logout for convenience.  	// file/download (see #2010)  	$exceptions = array(  		'admin/plugins/disable',  		'logout', -		'login',  		'file/download',  	);  	if (!in_array($action, $exceptions)) { -		// All actions require a token. -		action_gatekeeper(); +		action_gatekeeper($action);  	}  	$forwarder = str_replace(elgg_get_site_url(), "", $forwarder); @@ -189,6 +187,26 @@ function elgg_unregister_action($action) {  }  /** + * Is the token timestamp within acceptable range? + *  + * @param int $ts timestamp from the CSRF token + *  + * @return bool + */ +function _elgg_validate_token_timestamp($ts) { +	$action_token_timeout = elgg_get_config('action_token_timeout'); +	// default is 2 hours +	$timeout = ($action_token_timeout !== null) ? $action_token_timeout : 2; + +	$hour = 60 * 60; +	$timeout = $timeout * $hour; +	$now = time(); + +	// Validate time to ensure its not crazy +	return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)); +} + +/**   * Validate an action token.   *   * Calls to actions will automatically validate tokens. If tokens are not @@ -206,8 +224,6 @@ function elgg_unregister_action($action) {   * @access private   */  function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) { -	global $CONFIG; -  	if (!$token) {  		$token = get_input('__elgg_token');  	} @@ -216,29 +232,18 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)  		$ts = get_input('__elgg_ts');  	} -	if (!isset($CONFIG->action_token_timeout)) { -		// default to 2 hours -		$timeout = 2; -	} else { -		$timeout = $CONFIG->action_token_timeout; -	} -  	$session_id = session_id();  	if (($token) && ($ts) && ($session_id)) {  		// generate token, check with input and forward if invalid -		$generated_token = generate_action_token($ts); +		$required_token = generate_action_token($ts);  		// Validate token -		if ($token == $generated_token) { -			$hour = 60 * 60; -			$timeout = $timeout * $hour; -			$now = time(); - -			// Validate time to ensure its not crazy -			if ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)) { +		if ($token == $required_token) { +			 +			if (_elgg_validate_token_timestamp($ts)) {  				// We have already got this far, so unless anything -				// else says something to the contry we assume we're ok +				// else says something to the contrary we assume we're ok  				$returnval = true;  				$returnval = elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', array( @@ -252,10 +257,20 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)  					register_error(elgg_echo('actiongatekeeper:pluginprevents'));  				}  			} else if ($visibleerrors) { -				register_error(elgg_echo('actiongatekeeper:timeerror')); +				// this is necessary because of #5133 +				if (elgg_is_xhr()) { +					register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url()))); +				} else { +					register_error(elgg_echo('actiongatekeeper:timeerror')); +				}  			}  		} else if ($visibleerrors) { -			register_error(elgg_echo('actiongatekeeper:tokeninvalid')); +			// this is necessary because of #5133 +			if (elgg_is_xhr()) { +				register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url()))); +			} else { +				register_error(elgg_echo('actiongatekeeper:tokeninvalid')); +			}  		}  	} else {  		if (! empty($_SERVER['CONTENT_LENGTH']) && empty($_POST)) { @@ -284,12 +299,33 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)   * This function verifies form input for security features (like a generated token),   * and forwards if they are invalid.   * + * @param string $action The action being performed + *    * @return mixed True if valid or redirects.   * @access private   */ -function action_gatekeeper() { -	if (validate_action_token()) { -		return TRUE; +function action_gatekeeper($action) { +	if ($action === 'login') { +		if (validate_action_token(false)) { +			return true; +		} +		 +		$token = get_input('__elgg_token'); +		$ts = (int)get_input('__elgg_ts'); +		if ($token && _elgg_validate_token_timestamp($ts)) { +			// The tokens are present and the time looks valid: this is probably a mismatch due to the  +			// login form being on a different domain. +			register_error(elgg_echo('actiongatekeeper:crosssitelogin')); +			 +			 +			forward('login', 'csrf'); +		} +		 +		// let the validator send an appropriate msg +		validate_action_token(); +		 +	} elseif (validate_action_token()) { +		return true;  	}  	forward(REFERER, 'csrf'); @@ -328,16 +364,19 @@ function generate_action_token($timestamp) {  }  /** - * Initialise the site secret hash. + * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL).   *   * Used during installation and saves as a datalist.   * + * Note: Old secrets were hex encoded. + *   * @return mixed The site secret hash or false   * @access private   * @todo Move to better file.   */  function init_site_secret() { -	$secret = md5(rand() . microtime()); +	$secret = 'z' . ElggCrypto::getRandomString(31); +  	if (datalist_set('__site_secret__', $secret)) {  		return $secret;  	} @@ -364,6 +403,26 @@ function get_site_secret() {  }  /** + * Get the strength of the site secret + * + * @return string "strong", "moderate", or "weak" + * @access private + */ +function _elgg_get_site_secret_strength() { +	$secret = get_site_secret(); +	if ($secret[0] !== 'z') { +		$rand_max = getrandmax(); +		if ($rand_max < pow(2, 16)) { +			return 'weak'; +		} +		if ($rand_max < pow(2, 32)) { +			return 'moderate'; +		} +	} +	return 'strong'; +} + +/**   * Check if an action is registered and its script exists.   *   * @param string $action Action name  | 
