Discuz 用户多次登录失败 要求填写验证码
如遇抢票失败,可尝试稍后再次登录 #生活常识# #日常生活小窍门# #出行建议# #火车票抢购策略#
目标现在几乎所有的网站都会有验证码功能,有些是图文的,有些是填字符。在某个网站上看到过类似的功能,就想做一个。现在公司最近要求使用Discuz没办法,准备研究它的错误登录三次,就会要求等待15分钟时间的功能,并尝试修改它。
分析Discuz
第一步分析discuz 登录入口 member.php 文件 登录的地址为:member.php?mod=logging&action=login& loginsubmit=yes& infloat=yes& lssubmit=yes& inajax=1在这个文件里,系统会初始化框架,并判断mod是否存在,存在则加载对应文件。这里加载了./source/module/member/member_logging.php 文件
第二步分析member_logging.php 文件...... $ctl_obj = new logging_ctl(); $ctl_obj->setting = $_G['setting']; $method = 'on_'.$_GET['action']; $ctl_obj->template = 'member/login'; $ctl_obj->$method(); //这里直接实例化了class_member.php 的类,并调用 on_login 方法1234567 第三步 分析 class_member.php 文件 这里才是 登录的重点地方 分析on_login 方法
这个 方法里面写了大量的 用户登录业务逻辑,其他的暂时不管,只需要跟踪它的登录。由于Discuz做个Ucenter 这么一个用户中心,所以现在有两处登录的地方。
第一处
..... //这里用户登录用户的 $result = userlogin( $_GET['username'], $_GET['password'], $_GET['questionid'], $_GET['answer'], $this->setting['autoidselect'] ? 'auto' :$_GET['loginfield'], $_G['clientip']); $uid = $result['ucresult']['uid']; ......12345678910
第二处
function_member.php 文件分析
...... //这里实现用户 登录 if($isuid == 3) { if(!strcmp(dintval($username), $username) && getglobal('setting/uidlogin')) { $return['ucresult'] = uc_user_login($username, $password, 1, 1, $questionid, $answer, $ip); } elseif(isemail($username)) { $return['ucresult'] = uc_user_login($username, $password, 2, 1, $questionid, $answer, $ip); } if($return['ucresult'][0] <= 0 && $return['ucresult'][0] != -3) { $return['ucresult'] = uc_user_login(addslashes($username), $password, 0, 1, $questionid, $answer, $ip); } } else { $return['ucresult'] = uc_user_login(addslashes($username), $password, $isuid, 1, $questionid, $answer, $ip); } ......123456789101112131415
登录的到这里,就剩成功和失败了。今天不分析成功的情况,只分析处理登录失败的情况。
...... //登录失败 $password = preg_replace("/^(.{".round(strlen($_GET['password']) / 4)."})(.+?)(.{".round(strlen($_GET['password']) / 6)."})$/s", "\\1***\\3", $_GET['password']); $errorlog = dhtmlspecialchars( TIMESTAMP."\t". ($result['ucresult']['username'] ? $result['ucresult']['username'] : $_GET['username'])."\t". $password."\t". "Ques #".intval($_GET['questionid'])."\t". $_G['clientip']); //1、写入 日文件,暂时不研究 writelog('illegallog', $errorlog); //2、登录失败 处理 loginfailed($_GET['username']); //3、失败IP处理 failedip(); $fmsg = $result['ucresult']['uid'] == '-3' ? (empty($_GET['questionid']) || $answer == '' ? 'login_question_empty' : 'login_question_invalid') : 'login_invalid'; if($_G['member_loginperm'] > 1) { showmessage($fmsg, '', array('loginperm' => $_G['member_loginperm'] - 1)); } elseif($_G['member_loginperm'] == -1) { showmessage('login_password_invalid'); } else { showmessage('login_strike'); }
1234567891011121314151617181920212223242、登录失败处理
数据库文件 table_common_failedlogin.php文件
public function update_failed($ip, $username) { DB::query("UPDATE %t SET count=count+1, lastupdate=%d WHERE ip=%s", array($this->_table, TIMESTAMP, $ip)); } //从上面可以看出,这里直接更新了 xx_common_failedlogin.php表,在对应username字段下的 count 字段+1 ,比更新最后登录 时间 - lastupdate 字段 //%d 就是 TIMESTAMP //找到上面的定义 define('TIMESTAMP', time());123456
3、ip失败处理
function failedip() { global $_G; list($ip1, $ip2) = explode('.', $_G['clientip']); $ip = $ip1.'.'.$ip2; //这里直接记录了 ip ,具体的需要分析 table_common_failedip.php 文件。 C::t('common_failedip')->insert_ip($ip); }1234567
table_common_fialedip.php文件
public function insert_ip($ip) { if(DB::result_first("SELECT COUNT(*) FROM %t WHERE ip=%s AND lastupdate=%d", array($this->_table, $ip, TIMESTAMP))) { DB::query("UPDATE %t SET `count`=`count`+1 WHERE ip=%s AND lastupdate=%d", array($this->_table, $ip, TIMESTAMP)); } else { DB::query("INSERT INTO %t VALUES (%s, %d, 1)", array($this->_table, $ip, TIMESTAMP)); } DB::query("DELETE FROM %t WHERE lastupdate<%d", array($this->_table, TIMESTAMP - 3600)); }12345678
分析上面的代码:
这里首先判断,该IP下的登录失败是否存在,存在则更新登录失败的次数。不存在,则直接添加一条在该IP下的 登录失败数据。可以看到在最后 还一个删除 1个小时以前的失败记录。
上面是所有的登录失败操作。
那么问题出现了,在登录的时候是如何验证用户已经 连续多次登录失败过了呢?
Dicuz的验证用户是否多次登录失败在class_member.php 文件中
//检查 被锁定,不能登录 if(!($_G['member_loginperm'] = logincheck($_GET['username']))) { captcha::report($_G['clientip']); showmessage('login_strike'); } //logincheck() 这里面做的登录是否锁定验证 //logincheck 方法,可以看到,这里调用了 uc_user_logincheck方法。 function logincheck($username) { global $_G; $return = 0; $username = trim($username); loaducenter(); if(function_exists('uc_user_logincheck')) { $return = uc_user_logincheck(addslashes($username), $_G['clientip']); } ....... //查看到 uc_user_logincheck方法这里 调用了 /control/user.php文件 里面的 logincheck方法 function uc_user_logincheck($username, $ip) { return call_user_func(UC_API_FUNC, 'user', 'logincheck', array('username' => $username, 'ip' => $ip)); } //找到这个方法,发现是下面这样写的。 function onlogincheck() { $this->init_input(); $username = $this->input('username'); $ip = $this->input('ip'); return $_ENV['user']->can_do_login($username, $ip); } //$_ENV['user']->can_do_login($username, $ip); 分析这句话。$_ENV 为PHP全局变量 。可以知道,调用的是 user
123456789101112131415161718192021222324252627282930313233分析和查找$_ENV['user']代表的意义,根据Discuz 的尿性来说,这个一定是一个数据库对象,下面采用中断的方式开始分析这个$_ENV折出现的时机。
在client.php文件中下面位置
if(empty($uc_controls[$model])) { if(function_exists("mysql_connect")) { include_once UC_ROOT.'./lib/db.class.php'; } else { include_once UC_ROOT.'./lib/dbi.class.php'; } include_once UC_ROOT.'./model/base.php'; include_once UC_ROOT."./control/$model.php"; var_dump($_ENV['user']);exit;//没有结果 eval("\$uc_controls['$model'] = new {$model}control();"); var_dump($_ENV['user']);exit; //有输出 }123456789101112
开始 分析 uc_client\./control/user.php这个文件 。
//在这个文件中,看到下面这段 class usercontrol extends base { function __construct() { $this->usercontrol(); } function usercontrol() { parent::__construct(); $this->load('user'); //猜测是这里 加载了$_ENV 的东西。 $this->app = $this->cache['apps'][UC_APPID]; } ...... ...... //找到了 这个load 方法,果然,在这里调用了 function load($model, $base = NULL) { $base = $base ? $base : $this; if(empty($_ENV[$model])) { require_once UC_ROOT."./model/$model.php"; //这里加载了 /model/user.php 文件,去瞧一瞧。 eval('$_ENV[$model] = new '.$model.'model($base);'); } return $_ENV[$model]; }
12345678910111213141516171819202122232425文件 uc_client\model\user.php 文件
//哇咔咔,终于找到这个方法,开始分析这个方法。 ....... function can_do_login($username, $ip = '') { $check_times = $this->base->settings['login_failedtime'] < 1 ? 5 : $this->base->settings['login_failedtime']; //获取默认的最多失败次数 $username = substr(md5($username), 8, 15); $expire = 15 * 60; //这个是单次验证有效时间,单位秒 if(!$ip) { $ip = $this->base->onlineip; } $ip_check = $user_check = array(); $query = $this->db->query("SELECT * FROM ".UC_DBTABLEPRE."failedlogins WHERE ip='".$ip."' OR ip='$username'"); //根据IP,或者username 查询数据 ,根据这条sql可以知道这里要查询出的信息 while($row = $this->db->fetch_array($query)) { if($row['ip'] === $username) { $user_check = $row; } elseif($row['ip'] === $ip) { $ip_check = $row; } } //判断,是 名称验证,还是 ip验证 ,默认以 username 验证为准 if(empty($ip_check) || ($this->base->time - $ip_check['lastupdate'] > $expire)) { $ip_check = array(); $this->db->query("REPLACE INTO ".UC_DBTABLEPRE."failedlogins (ip, count, lastupdate) VALUES ('{$ip}', '0', '{$this->base->time}')"); } if(empty($user_check) || ($this->base->time - $user_check['lastupdate'] > $expire)) { $user_check = array(); $this->db->query("REPLACE INTO ".UC_DBTABLEPRE."failedlogins (ip, count, lastupdate) VALUES ('{$username}', '0', '{$this->base->time}')"); } if ($ip_check || $user_check) { $time_left = min(($check_times - $ip_check['count']), ($check_times - $user_check['count'])); return $time_left; } //上面看出,返回值为,最后还有登陆次数的机会。 $this->db->query("DELETE FROM ".UC_DBTABLEPRE."failedlogins WHERE lastupdate<".($this->base->time - ($expire + 1)), 'UNBUFFERED'); return $check_times; }
1234567891011121314151617181920212223242526272829303132333435363738394041424344最后 回归到 class_member.php 中下面
//检查 被锁定,不能登录 if(!($_G['member_loginperm'] = logincheck($_GET['username']))) { captcha::report($_G['clientip']); showmessage('login_strike'); } //返回结果为 还可以错误的次数,若果返回 0 则直接 返回错误哦提示了。123456
到此 Discuz 的登录失败 处理,都分析完了。
网址:Discuz 用户多次登录失败 要求填写验证码 https://www.yuejiaxmz.com/news/view/519224
相关内容
Centos7(Ubuntu)密码登录失败锁定设置(亲测)PowerBuilder连接SQLServer失败 SQL State:‘28000’
验证码是4+5=怎么输入
【Discuz】在linux中搭建 Discuz 论坛(LAMP=linux+Apache+mysql+PHP)
python文档的读写操作,用户密码登录程序
华为智慧生活登录失败怎么办 华为智慧生活app登录不了解决办法
js常规登录验证
用户登录
[win7]设置登录密码
对登录功能进行压力测试: