Перейти к содержимому






Без капчи и яваскрипта

Написано Arhar, 07 Октябрь 2010 · 2 245 Просмотров

Защита от ботов доставляет юзеру неудобства - кривая картинка или новомодный яваскрипт, отключенный в стародавних браузерах древних гос-предприятий (что кажется странным, ибо у нас вообще куплен win7).
Плюс яваскрипт пока-что подчиняется простым алгоритмам, установка значения в hidden поле, например.
Так вот, хочу обсудить следующую идею:
Для регистрации требуется заполнить несколько полей с фиксированными названиями в html коде.
Важных из них 5 - login, email, email-2, пароль, пароль-2.
Спам программа заполняет их значениями, которые должна запомнить для последующей авторизации (поля тире-2 должны совпасть, поле логин и пароль используется дальше).
А теперь сделаем так, чтобы машина не знала, какое поле логин, какое пароль, какое email.
В таблицу текущих регистраций (куда кладется текущее значение капчи) добавим 5 пунктов для каждого из полей соответственно. В каждом пункте будет текущее html-имя поля ввода, по которому будут браться данные (input[$newnames['login']] например). Формат имени поля будет нефиксированной длины, чтобы нельзя было использовать этот признак в программе. Составляться имя поля будет из маленьких и больших латинских букв и цифр каждый раз рандомно (и рандомной длины ;) ).
Проверка email и пароля на лету делаться не будет, ибо тогда появится возможность узнать каким-либо образом (зависит от реализации проверки) соответствие html имен. Проверка будет делаться на сервере, ибо ошибки в этом месте у человека бывают крайне редко, а если бывают, ничего - введет снова. Мы делаем систему при которой юзер, делающий все верно, обламываться и замечать что-либо вообще не будет, а машина не пройдет.




Добавочно к проверке на лету - можно отправлять значения всех пяти полей, а сравнивать на сервере только необходимые, возвращать только true или false, и вот она - проверка на лету не дает узнать соответствие имен :)
  • Жалоба
  • Жалоба
ну это не совсем то, хотя и работает
здесь яваскрипт меняет имена, а цель - вообще без яваскрипта
  • Жалоба
Зато можно ставить на абсолютно любой уже существующий PHP-скрпит. Декодирование $_POST-имен осуществляется до запуска скрипта, а кодирование - на стороне пользователя :)
  • Жалоба
Это как универсальный модуль хорошо, но если усложнить механизм яваскрипт защиты, можно делать просто сложные яваскрипт вычисления.
Однако чем сложнее яваскрипт, тем больше проблем у пользователя, зайду я с оперы мини, а там не вычислится что-нибудь.
Идея с подготовкой скрипта на сервере хороша тем, что мы заботимся о человеке, пользователь не должен испытывать вообще каких-либо затыков, ничто не должно его обламывать, он просто вводит буквы и все работает, он даже не подозревает, что система начисто отрезает злоумышленную машину, шикарная система.
  • Жалоба
С 99% вероятностью в данном случае будет то же самое. Просто обычная форма. Остается только поместить предупреждение об отключенном JavaScript для оставшегося процента, и вот у нас 100% покрытие :)
  • Жалоба
посмотрел код, что-то много вычислений, однако может я не увидел, а как числа получаются каждый раз случайные?
  • Жалоба
и еще,в куки кладутся и передаются готовые имена, так? тогда нам просто достаточно передать свои готовые имена и по ним свои готовые данные
  • Жалоба
Попробуйте :3

Вообще скрипт совершенно прост. Он берет с сервера ekey и выполняет md5(name+ekey) для всех имен полей input и textarea, название которых начинается на "enc_". На стороне сервера удаляются все $_POST-данные, начинающиеся на "enc_" (если они найдены, при этом выставляется хинт "предупредить про javascript"), а затем разворачиваются все md5-закодированные поля путем сопоставления md5(val+ekey) из cookie-массива vals с именами входящих полей. При каждой удачной отправке формы ekey перегенерировывается.

Такую защиту, кстати, можно автоматизированно обойти, если брать ekey из cookies вручную и кодировать поля самостоятельно, однако, на каждом клентском сайте к md5 можно подмешивать собственную "salt" на обоих сторонах, а также использовать для генерации ekey IP-адрес посетителя и его UserAgent.
  • Жалоба
опять таки, данные, известные потенциальному нарушителю, при таком раскладе нет имитостойкости (можно скомпроментировать систему, подложив свои данные, которые пройдут алгоритм декодирования и система примет их за достоверные)
в случае заранее рандомного задания имен полей нельзя установить однозначную привязку, какое поле какое, значит есть только крайне малая доля вероятности удачной атаки
  • Жалоба
о чем я говорю - может алгоритм кодирования и будет известен нарушителю, если ему не будет известен текущий ключ кодирования - все бесполезно, а в данном случае ключ - совокупность случайно выбранных параметров создания имени - длина, тип используемых символов, номера используемых символов из таблицы
  • Жалоба
при длине от 5 до 16 символов, 7 типах используемых символов (только цифры(m=10), только маленькие буквы(m=26), только большие буквы(m=26), маленькие и цифры(m=36), большие и цифры(m=36), маленькие и большие(m=52), все(m=62)) и выборе на каждую позицию длины n из m символов - m в степени n возможных вариантов названия поля и для каждого из 5 полей каждый раз случайный алгоритм
  • Жалоба
еще одно - порядок появления имен на странице - тоже надо делать каждый раз новый
  • Жалоба
отсюда еще одна проблема - можно установить соответствие, отпарсив подсказки для поля (что куда вводить), значит надо придумать способ расположения полей, при котором внешний вид будет неизменен, а в коде они будут лежать каждый раз в новом месте
  • Жалоба

pure.php

<?php

class captchaPlugin
{
	var $ipsclass;
	var $error_string = '';
	
	function captchaPlugin( $ipsclass )
	{
		$this->ipsclass =& $ipsclass;
	}
	
	function getTemplate()
	{
		//-----------------------------------------
		// Clear old
		//-----------------------------------------
		
		$this->_clearSessions( $this->ipsclass->ip_address );
		
		//-----------------------------------------
		// Create new ID
		//-----------------------------------------
		
		$captcha_unique_id = md5( uniqid( time() ) );
		$captcha_string    = "";
		
		//-----------------------------------------
		// Create new string
		//-----------------------------------------
		
		/* Seed the random number generator */
		$unique_id 	= uniqid( mt_rand(), TRUE );
		$unique_id .= md5( $this->ipsclass->vars['sql_pass'] );
		usleep( mt_rand( 15000,1000000 ) );		
		mt_srand( (double) microtime() * 1000000 );		

		/* Array of characters */
		$array_of_chars = array( 
								'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
								'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
								'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
								);
		
		/* Get 6 random characters */
		for( $i = 0; $i < 6; $i++ )
		{
			$idx = rand( 0, count( $array_of_chars ) );
			$captcha_string .= $array_of_chars[ $idx ];
		}
								
		$login='';
		$length=mt_rand(5,15);
		for($i=0;$i<$length;$i++)
		{
			$idx = mt_rand( 0, count( $array_of_chars ) );
			$login .= $array_of_chars[ $idx ];
		}
		
		$pass1='';
		$length=mt_rand(5,15);
		for($i=0;$i<$length;$i++)
		{
			$idx = mt_rand( 0, count( $array_of_chars ) );
			$pass1 .= $array_of_chars[ $idx ];
		}
		
		$pass2='';
		$length=mt_rand(5,15);
		for($i=0;$i<$length;$i++)
		{
			$idx = mt_rand( 0, count( $array_of_chars ) );
			$pass2 .= $array_of_chars[ $idx ];
		}
		
		$email1='';
		$length=mt_rand(5,15);
		for($i=0;$i<$length;$i++)
		{
			$idx = mt_rand( 0, count( $array_of_chars ) );
			$email1 .= $array_of_chars[ $idx ];
		}
		
		$email2='';
		$length=mt_rand(5,15);
		for($i=0;$i<$length;$i++)
		{
			$idx = mt_rand( 0, count( $array_of_chars ) );
			$email2 .= $array_of_chars[ $idx ];
		}
								
		//-----------------------------------------
		// Add to the DB
		//-----------------------------------------
	
		$this->ipsclass->DB->do_insert( 'reg_antispam', array( 'regid'      => $captcha_unique_id,
														       'regcode'    => $captcha_string,
														       'ip_address' => $this->ipsclass->ip_address,
														       'ctime'      => time(),
														       'login'		=> $login,
															   'password_1'	=> $pass1,
															   'password_2'	=> $pass2,
															   'email_1'	=> $email1,
															   'email_2'	=> $email2,
															   ) );

		return $captcha_unique_id;
	}
	
	function validate()
	{
		//-----------------------------------------
		// INIT
		//-----------------------------------------
		
		$captcha_unique_id       = $this->ipsclass->txt_alphanumerical_clean( $_REQUEST['regid'] );
		$captcha_input_NOT_CLEAN = trim(  $_REQUEST['reg_code']  );
		
		//-----------------------------------------
		// Get the info from the DB
		//-----------------------------------------
		
		$captcha = $this->ipsclass->DB->build_and_exec_query( array( 'select' => '*',
																	 'from'   => 'reg_antispam',
																	 'where'  => "regid='". addslashes( $captcha_unique_id )."'" ) );
	
		if ( ! $captcha['regid'] )
		{
			return FALSE;
		}
		
		//-----------------------------------------
		// Check...
		//-----------------------------------------
		
		if ( $captcha['regcode'] != $captcha_input_NOT_CLEAN )
		{
			return FALSE;
		}
		else
		{
			$this->_clearSessions( $this->ipsclass->ip_address );
			return TRUE;
		}
	}

	/*-------------------------------------------------------------------------*/
	// Removes extinct captcha sessions
	/*-------------------------------------------------------------------------*/
	/**
	* Removes extinct captcha sessions
	* Clears out "dead" entries
	*
	* @param	string	Optional IP address to clear out on
	* @return	string	void
	*/
	
	function _clearSessions( $ip_address='' )
	{
		//-----------------------------------------
		// INIT
		//-----------------------------------------
		
		$time  = time() - 60 * 3600;
		$extra = ( $ip_address ) ? ' OR ip_address="'.$ip_address.'"' : '';
		
		//-----------------------------------------
		// Remove...
		//-----------------------------------------
		
		$this->ipsclass->DB->build_and_exec_query( array( 'delete' => 'reg_antispam',
													  	  'where'  => 'ctime < ' . $time . $extra ) );
		
	}
}

?>

register.php

		if($this->ipsclass->vars['bot_antispam_type']=='pure')
		{
			$data = array( 'TEXT' => $this->ipsclass->lang['std_text'], 'coppa_user' => $coppa );
			
			$captcha = $this->ipsclass->DB->build_and_exec_query( array( 'select' => '*',
																	 'from'   => 'reg_antispam',
																	 'where'  => "regid='". addslashes( $captchaHTML )."'" ) );
			
			$data['login']=$this->ipsclass->compiled_templates['skin_register']->showformpure_field($captcha['login'],$this->ipsclass->lang['user_name_text'],$this->ipsclass->lang['user_name'],$this->ipsclass->vars['max_user_name_length'],$this->ipsclass->input['UserName'],'text','0');
			$data['pass1']=$this->ipsclass->compiled_templates['skin_register']->showformpure_field($captcha['password_1'],$this->ipsclass->lang['password_text'],$this->ipsclass->lang['password'],32,"",'password','70');
			$data['pass2']=$this->ipsclass->compiled_templates['skin_register']->showformpure_field($captcha['password_2'],$this->ipsclass->lang['password_confirm_text'],$this->ipsclass->lang['password_confirm'],32,"",'password','140');
			$data['email1']=$this->ipsclass->compiled_templates['skin_register']->showformpure_field($captcha['email_1'],$this->ipsclass->lang['email_address_text'],$this->ipsclass->lang['email_address'],150,$this->ipsclass->input['EmailAddress'],'text','210');
			$data['email2']=$this->ipsclass->compiled_templates['skin_register']->showformpure_field($captcha['email_2'],$this->ipsclass->lang['email_address_confirm_text'],$this->ipsclass->lang['email_address_confirm'],150,$this->ipsclass->input['EmailAddress_two'],'text','280');
			$field=array($data['login'],$data['pass1'],$data['pass2'],$data['email1'],$data['email2']);
			shuffle($field);
			$data['regid']=$captchaHTML;
			$data['reg_code']=$captcha['regcode'];
			
			$this->output .= $this->ipsclass->compiled_templates['skin_register']->showformpure($data, $final_errors, $field);
		
		}
		else
		{
		
		$this->ipsclass->input['UserName'] 				= isset($this->ipsclass->input['UserName']) 			? $this->ipsclass->input['UserName'] 				: '';
		$this->ipsclass->input['PassWord'] 				= isset($this->ipsclass->input['PassWord']) 			? $this->ipsclass->input['PassWord'] 				: '';
		$this->ipsclass->input['EmailAddress'] 			= isset($this->ipsclass->input['EmailAddress']) 		? $this->ipsclass->input['EmailAddress'] 			: '';
		$this->ipsclass->input['EmailAddress_two']		= isset($this->ipsclass->input['EmailAddress_two'])		? $this->ipsclass->input['EmailAddress_two']		: '';
		$this->ipsclass->input['PassWord_Check'] 		= isset($this->ipsclass->input['PassWord_Check']) 		? $this->ipsclass->input['PassWord_Check'] 			: '';
		$this->ipsclass->input['members_display_name'] 	= isset($this->ipsclass->input['members_display_name'])	? $this->ipsclass->input['members_display_name']	: '';
		$this->ipsclass->input['time_offset'] 			= isset($this->ipsclass->input['time_offset']) 			? $this->ipsclass->input['time_offset'] 			: '';
		$this->ipsclass->input['allow_member_mail'] 	= isset($this->ipsclass->input['allow_member_mail'])	? $this->ipsclass->input['allow_member_mail']		: '';		
		$this->ipsclass->input['dst'] 					= isset($this->ipsclass->input['dst'])					? $this->ipsclass->input['dst']						: '';		
		
    	$this->output .= $this->ipsclass->compiled_templates['skin_register']->ShowForm( array( 'TEXT' => $this->ipsclass->lang['std_text'], 'coppa_user' => $coppa ), $final_errors );
		
    	//-----------------------------------------
    	// Replace elements
    	//-----------------------------------------
    	
    	if ($this->ipsclass->vars['bot_antispam'] )
		{
			$this->output = str_replace( "<!--{REG.ANTISPAM}-->", $captchaHTML, $this->output );
		}
		
		}

showformpure($data="",$errors=array(),$field)

<script type="text/javascript">
var ipb_lang_js_blanks   = "{$this->ipsclass->lang['js_blanks']}";
var ipb_lang_js_no_check = "{$this->ipsclass->lang['js_no_check']}";
var subsdesc_0 = "{$this->ipsclass->lang['subsm_no_desc']}";
var subdesc   = new Array(); <!--{SUBS.JSCRIPT}-->
var register_method = "{$this->ipsclass->vars['ipbli_usertype']}";
var allowed_chars = "{$this->ipsclass->vars['username_characters']}";
var allowed_error = "{$this->ipsclass->vars['username_errormsg']}";
</script>
<script type="text/javascript" src="jscripts/ipb_register.js"></script>
<form action="{$this->ipsclass->vars['board_url']}/index.{$this->ipsclass->vars['php_ext']}" method="post" name="REG" onsubmit="return validate_reg_form(event)">
<input type="hidden" name="act" value="Reg" />
<input type="hidden" name="termsread" value="1" />
<input type="hidden" name="agree_to_terms" value="1" />
<input type="hidden" name="CODE" value="02" />
<input type="hidden" name="regid" value="{$data['regid']}" />
<input type="hidden" name="reg_code" value="{$data['reg_code']}" />
<input type="hidden" name="coppa_user" value="{$data['coppa_user']}" />
<div class="borderwrap">
    <div class="maintitle">{$this->ipsclass->lang['registration_form']}</div>
    <div class='row2' style='padding:4px'>
        <div class="errorwrap" style='margin:0px;padding-bottom:0px'><h4>{$this->ipsclass->lang['form_title_attention']}</h4><p>{$data['TEXT']}</p></div>
    </div>
    <div class="row2">
        <table class='ipbtable' cellspacing="0">
            <tr>
                <td width="50%" valign='top'>
<if="$errors['username'] or $errors['password'] or $errors['email']  ">
<fieldset class="row3">
                    <legend><b>Ошибки</b></legend>
</if>
<if="$errors['username'] != ''">
<div class='input-warn-content' id='box-name'><div id='msg-name'>{$errors['username']}</div></div>
</if>
<if="$errors['password'] != ''">
<div class='input-warn-content' id='box-password'><div id='msg-password'>{$errors['password']}</div></div>
</if>    
<if="$errors['email']!=''">
<div class='input-warn-content' id='box-emailaddress'><div id='msg-emailaddress'>{$errors['email']}</div></div>
</if>    
<if="$errors['username'] or $errors['password'] or $errors['email']  ">
</fieldset><br/>
</if>
                    <fieldset class="row3" style="height: 350px;">
                    <legend><b>Основные данные</b></legend>


                        {$field[0]}

<if="$errors['dname'] != ''">
<div class='input-warn-content' id='box-dname'><div id='msg-dname'>{$errors['dname']}</div></div>
</if>
<if="$this->ipsclass->vars['auth_allow_dnames'] == 1 OR $this->ipsclass->vars['ipbli_usertype'] == 'email'">
                          <table class='ipbtable' cellspacing="0">
                            <tr>
                                <td>{$this->ipsclass->lang['dname_name']} &nbsp;<span>(<a href="#" style="cursor: help;" title="{$this->ipsclass->lang['dname_text']}">?</a>)</span></td>
                            </tr>
                            <tr>
                                <td>
                                    <input type="text" size="50" maxlength="{$this->ipsclass->vars['max_user_name_length']}" value="{$this->ipsclass->input['members_display_name']}" id='reg-members-display-name' name="members_display_name" />
                                    <img id='img-members-display-name' src="{$this->ipsclass->vars['img_url']}/spacer.gif" alt="" width='12' height='12' />
                                </td>
                            </tr>
                         </table>
</if>
                        {$field[1]}
                        {$field[2]}
                        {$field[3]}
                        {$field[4]}
                    </fieldset><br />

                    <!--{REQUIRED.FIELDS}-->
                    <!--{SUBS.MANAGER}-->
                    <!--IBF.MODULES.EXTRA-->
                    <!--{REG.ANTISPAM}-->
                </td>
                <td width="50%" valign="top">
                    <div>
                        <b>{$this->ipsclass->lang['cf_optional']}</b><br /><br />
                        <table class='ipbtable' cellspacing="0">
                            <tr>
                                <td>
                                    <fieldset>
                                    <legend>{$this->ipsclass->lang['op_email_title']}</legend>
                                        <div class="desc">{$this->ipsclass->lang['op_email_text']}</div><br />
                                        <input type="checkbox" name="allow_admin_mail" value="1" class="checkbox" <!--[admin.checked]--> /> {$this->ipsclass->lang['op_email_ad']}<br />
                                        <input type="checkbox" name="allow_member_mail" value="1" class="checkbox" <!--[member.checked]--> /> {$this->ipsclass->lang['op_email_mem']}
                                    </fieldset><br />
                            
                                    <fieldset>
                                    <legend>{$this->ipsclass->lang['op_tz_title']}</legend>
                                        <div class="desc">{$this->ipsclass->lang['op_tz_text']}</div><br />
                                        <!--{TIME_ZONE}--><br /><br />
                                        <!--<input type="checkbox" name="dst" value="1" class="checkbox" <!--[dst.checked]--> /> {$this->ipsclass->lang['op_tz_dst']}<br />-->
                                    </fieldset><br />
                                    <!--{OPTIONAL.FIELDS}-->
                                </td>
                            </tr>
                        </table>
                    </div>
                </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td valign="middle" align="center">
                    <div class="desc">{$this->ipsclass->lang['submit_text']}</div><br />
                        <input class='button' type="submit" value="{$this->ipsclass->lang['submit_form']} &gt; &gt;" />
                </td>
            </tr>
        </table>
    </div>
</div>
</form>

showformpure_field($name,$title,$lang,$max,$input,$type,$top)

<table class='ipbtable' cellspacing="0" style="position: absolute; margin-top: {$top}px;">
							<tr>
								<td>{$lang} &nbsp;<span>(<a href="#" style="cursor: help;" title="{$title}">?</a>)</span></td>
							</tr>
							<tr>
								<td>
								  <input type="{$type}" size="45" maxlength="{$max}" value="{$input}" id='{$name}' name="{$name}" />
								  <img id='{$name}' src="{$this->ipsclass->vars['img_url']}/spacer.gif" alt="" width='12' height='12' />
								</td>
							</tr>
						  </table>

IN_DEV settings

default=Нормальная (с использованием GD)
recaptcha=reCAPTCHA
pure=pure

  • Жалоба

register.php

function create_account()
	{
		if($this->ipsclass->vars['bot_antispam_type']=='pure')
		{
			$captcha_unique_id       = $this->ipsclass->txt_alphanumerical_clean( $_POST['regid'] );
			$captcha = $this->ipsclass->DB->build_and_exec_query( array( 'select' => '*',
																		 'from'   => 'reg_antispam',
																		 'where'  => "regid='". addslashes( $captcha_unique_id )."'" ) );
			$this->ipsclass->input['UserName']=$this->ipsclass->input[$captcha['login']];
			$this->ipsclass->input['PassWord']=$this->ipsclass->input[$captcha['password_1']];
			$this->ipsclass->input['PassWord_Check']=$this->ipsclass->input[$captcha['password_2']];
			$this->ipsclass->input['EmailAddress']=$this->ipsclass->input[$captcha['email_1']];
			$this->ipsclass->input['EmailAddress_two']=$this->ipsclass->input[$captcha['email_2']];
		}
  • Жалоба
ALTER TABLE ibf_reg_antispam ADD login VARCHAR(32) NOT NULL DEFAULT '';
ALTER TABLE ibf_reg_antispam ADD password_1 VARCHAR(32) NOT NULL DEFAULT '';
ALTER TABLE ibf_reg_antispam ADD password_2 VARCHAR(32) NOT NULL DEFAULT '';
ALTER TABLE ibf_reg_antispam ADD email_1 VARCHAR(32) NOT NULL DEFAULT '';
ALTER TABLE ibf_reg_antispam ADD email_2 VARCHAR(32) NOT NULL DEFAULT '';
  • Жалоба

Май 2018

П В С Ч П С В
 123456
78910111213
14151617181920
2122232425 26 27
28293031   

Поиск по блогу

Теги