Browse Source

+ Anti spam measures, namely a time gate on registration and an empty field during any CAPTCHA instance.

Peter Spicer 10 years ago
parent
commit
254e15ca38

+ 21 - 0
Sources/Register.php

@@ -95,6 +95,15 @@ function Register($reg_errors = array())
 		'name' => $txt['register'],
 	);
 
+	// Prepare the time gate! Do it like so, in case later steps want to reset the limit for any reason, but make sure the time is the current one.
+	if (!isset($_SESSION['register']))
+		$_SESSION['register'] = array(
+			'timenow' => time(),
+			'limit' => 10, // minimum number of seconds required on this page for registration
+		);
+	else
+		$_SESSION['register']['timenow'] = time();
+
 	// If you have to agree to the agreement, it needs to be fetched from the file.
 	if ($context['require_agreement'])
 	{
@@ -252,6 +261,17 @@ function Register2($verifiedOpenID = false)
 			fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
 		}
 
+		// Check the time gate for miscreants. First make sure they came from somewhere that actually set it up.
+		if (empty($_SESSION['register']['timenow']) || empty($_SESSION['register']['limit']))
+			redirectexit('action=register');
+		// Failing that, check the time on it.
+		if (time() - $_SESSION['register']['timenow'] < $_SESSION['register']['limit'])
+		{
+			// @todo This too should be put in Errors, imho.
+			loadLanguage('Login');
+			$reg_errors[] = $txt['error_too_quickly'];
+		}
+
 		// Check whether the visual verification code was entered correctly.
 		if (!empty($modSettings['reg_verification']))
 		{
@@ -445,6 +465,7 @@ function Register2($verifiedOpenID = false)
 	if (!empty($reg_errors))
 	{
 		$_REQUEST['step'] = 2;
+		$_SESSION['register']['limit'] = 5; // If they've filled in some details, they won't need the full 10 seconds of the limit.
 		return Register($reg_errors);
 	}
 	// If they're wanting to use OpenID we need to validate them first.

+ 26 - 0
Sources/Subs-Editor.php

@@ -1962,6 +1962,7 @@ function create_control_verification(&$verificationOptions, $do_test = false)
 	if ($isNew)
 		$context['controls']['verification'][$verificationOptions['id']] = array(
 			'id' => $verificationOptions['id'],
+			'empty_field' => empty($verificationOptions['no_empty_field']),
 			'show_visual' => !empty($verificationOptions['override_visual']) || (!empty($modSettings['visual_verification_type']) && !isset($verificationOptions['override_visual'])),
 			'number_questions' => isset($verificationOptions['override_qs']) ? $verificationOptions['override_qs'] : (!empty($modSettings['qa_verification_number']) ? $modSettings['qa_verification_number'] : 0),
 			'max_errors' => isset($verificationOptions['max_errors']) ? $verificationOptions['max_errors'] : 3,
@@ -2031,6 +2032,12 @@ function create_control_verification(&$verificationOptions, $do_test = false)
 		// ... nor this!
 		if ($thisVerification['number_questions'] && (!isset($_SESSION[$verificationOptions['id'] . '_vv']['q']) || !isset($_REQUEST[$verificationOptions['id'] . '_vv']['q'])))
 			fatal_lang_error('no_access', false);
+		// Hmm, it's requested but not actually declared. This shouldn't happen.
+		if ($thisVerification['empty_field'] && empty($_SESSION[$verificationOptions['id'] . '_vv']['empty_field']))
+			fatal_lang_error('no_access', false);
+		// While we're here, did the user do something bad?
+		if ($thisVerification['empty_field'] && !empty($_SESSION[$verificationOptions['id'] . '_vv']['empty_field']) && !empty($_REQUEST[$_SESSION[$verificationOptions['id'] . '_vv']['empty_field']]))
+			$verification_errors[] = 'wrong_verification_answer';
 
 		if ($thisVerification['show_visual'] && (empty($_REQUEST[$verificationOptions['id'] . '_vv']['code']) || empty($_SESSION[$verificationOptions['id'] . '_vv']['code']) || strtoupper($_REQUEST[$verificationOptions['id'] . '_vv']['code']) !== $_SESSION[$verificationOptions['id'] . '_vv']['code']))
 			$verification_errors[] = 'wrong_verification_code';
@@ -2083,6 +2090,17 @@ function create_control_verification(&$verificationOptions, $do_test = false)
 		$_SESSION[$verificationOptions['id'] . '_vv']['q'] = array();
 		$_SESSION[$verificationOptions['id'] . '_vv']['code'] = '';
 
+		// Make our magic empty field.
+		if ($thisVerification['empty_field'])
+		{
+			// We're building a field that lives in the template, that we hope to be empty later. But at least we give it a believable name.
+			$terms = array('gadget', 'device', 'uid', 'gid', 'guid', 'uuid', 'unique', 'identifier');
+			$second_terms = array('hash', 'cipher', 'code', 'key', 'unlock', 'bit', 'value');
+			$start = mt_rand(0, 27);
+			$hash = substr(md5(time()), $start, 4);
+			$_SESSION[$verificationOptions['id'] . '_vv']['empty_field'] = $terms[array_rand($terms)] . '-' . $second_terms[array_rand($second_terms)] . '-' . $hash;
+		}
+
 		// Generating a new image.
 		if ($thisVerification['show_visual'])
 		{
@@ -2112,6 +2130,14 @@ function create_control_verification(&$verificationOptions, $do_test = false)
 		$thisVerification['text_value'] = !empty($_REQUEST[$verificationOptions['id'] . '_vv']['code']) ? $smcFunc['htmlspecialchars']($_REQUEST[$verificationOptions['id'] . '_vv']['code']) : '';
 	}
 
+	// If we do have an empty field, it would be nice to hide it from legitimate users who shouldn't be populating it anyway.
+	if (!empty($_SESSION[$verificationOptions['id'] . '_vv']['empty_field']))
+	{
+		if (!isset($context['html_headers']))
+			$context['html_headers'] = '';
+		$context['html_headers'] .= '<style type="text/css">.vv_special { display:none; }</style>';
+	}
+
 	// Have we got some questions to load?
 	if (!empty($questionIDs))
 	{

+ 9 - 0
Themes/default/GenericControls.template.php

@@ -238,6 +238,15 @@ function template_control_verification($verify_id, $display_type = 'all', $reset
 			echo '
 			<div id="verification_control_', $i, '" class="verification_control">';
 
+		// Display empty field, but only if we have one, and it's the first time.
+		if ($verify_context['empty_field'] && empty($i))
+			echo '
+				<div class="smalltext vv_special">
+					', $txt['visual_verification_hidden'], ':
+					<input type="text" name="', $_SESSION[$verify_id . '_vv']['empty_field'], '" autocomplete="off" size="30" value="" />
+				</div>
+				<br />';
+
 		// Do the actual stuff - image first?
 		if ($i == 0 && $verify_context['show_visual'])
 		{

+ 1 - 0
Themes/default/languages/Login.english.php

@@ -13,6 +13,7 @@ $txt['agree_coppa_below'] = 'I am younger than %1$d years old.';
 
 // Registration form.
 $txt['registration_form'] = 'Registration Form';
+$txt['error_too_quickly'] = 'You went through registration a bit too quickly, faster than should normally be possible. Please give it a moment and try again.';
 $txt['need_username'] = 'You need to fill in a username.';
 $txt['no_password'] = 'You didn\'t enter your password.';
 $txt['incorrect_password'] = 'Password incorrect';

+ 1 - 0
Themes/default/languages/index.english.php

@@ -773,6 +773,7 @@ $txt['mod_reports_waiting'] = 'There are currently %1$d moderator reports open.'
 $txt['view_unread_category'] = 'Unread Posts';
 $txt['new_posts_in_category'] = 'Click to see the new posts in %1$s';
 $txt['verification'] = 'Verification';
+$txt['visual_verification_hidden'] = 'Please leave this box empty';
 $txt['visual_verification_description'] = 'Type the letters shown in the picture';
 $txt['visual_verification_sound'] = 'Listen to the letters';
 $txt['visual_verification_request_new'] = 'Request another image';