dedecms登陆原理初步研究
dedecms
默认dedecms是关闭会员功能的,如图所示

开启很简单,进入后台,系统-》系统基本参数-》会员设置-》是否开启会员功能,把是选上即可。
然后首页就有一个登陆功能

然后尝试登陆了一下,报这个错

首页哪来的验证码阿,我靠,dedecms你不会懒得搞验证码,才把会员功能默认关掉的吧
然后就进入一个中规中矩的登陆界面

然后测试一下,乱填一个用户名,果然

填一个存在的,密码乱写

真是不够严谨呢,我对接下来的严谨表示忧虑
登陆form表单,具体为
<form action="index_do.php" method="POST" name="form1">
可见index_do.php为核心登陆代码,二话不说打开吧!
开篇
require_once(dirname(__FILE__)."/config.php"); if(empty($dopost)) $dopost = ''; if(empty($fmdo)) $fmdo = '';
引入一个配置文件,断点得出登陆页面提交后,$dopost='login',$fmdo='login'
下面开始判断$fmdo的值了,呵,这个页面还处理挺多的功能,比如说发送邮件阿,注册阿,积分换金币阿,登陆阿,等等。其余暂时不表,先看登陆。不过,你能不能换switch,if elseif elseif 效率明显没有switch高。
看代码,
else if($fmdo=='login')
{
//用户登录
if($dopost=="login")
{
if(!isset($vdcode))
{
$vdcode = '';
}
$svali = GetCkVdValue();
if(preg_match("/2/",$safe_gdopen)){
if(strtolower($vdcode)!=$svali || $svali=='')
{
ResetVdValue();
ShowMsg('验证码错误!', 'index.php');
exit();
}
}这是判断验证码的,继续往下看
if(CheckUserID($userid,'',false)!='ok')
{
ResetVdValue();
ShowMsg("你输入的用户名 {$userid} 不合法!","index.php");
exit();
}
if($pwd=='')
{
ResetVdValue();
ShowMsg("密码不能为空!","-1",0,2000);
exit();
}CheckUserID方法存在于/include/memberlogin_class.php中,具体代码为:
function CheckUserID($uid, $msgtitle='用户名', $ckhas=TRUE)
{
global $cfg_mb_notallow,$cfg_mb_idmin,$cfg_md_idurl,$cfg_soft_lang,$dsql;
if($cfg_mb_notallow != '')
{
$nas = explode(',', $cfg_mb_notallow);
if(in_array($uid, $nas))
{
return $msgtitle.'为系统禁止的标识!';
}
}
if($cfg_md_idurl=='Y' && preg_match("/[^a-z0-9]/i",$uid))
{
return $msgtitle.'必须由英文字母或数字组成!';
}
if($cfg_soft_lang=='utf-8')
{
$ck_uid = utf82gb($uid);
}
else
{
$ck_uid = $uid;
}
for($i=0; isset($ck_uid[$i]); $i++)
{
if(ord($ck_uid[$i]) > 0x80)
{
if(isset($ck_uid[$i+1]) && ord($ck_uid[$i+1])>0x40)
{
$i++;
}
else
{
return $msgtitle.'可能含有乱码,建议你改用英文字母和数字组合!';
}
}
else
{
if(preg_match("/[^0-9a-z@\.-]/i",$ck_uid[$i]))
{
return $msgtitle.'不能含有 [@]、[.]、[-]以外的特殊符号!';
}
}
}
if($ckhas)
{
$row = $dsql->GetOne("SELECT * FROM `#@__member` WHERE userid LIKE '$uid' ");
if(is_array($row)) return $msgtitle."已经存在!";
}
return 'ok';
}$cfg_mb_notallow 是在后台在进行设置的,不允许注册,也不允许登陆。
$cfg_md_idurl //这个是为空的,所以中文是可以被注册的,
$ckhas //这个也是为空,注册的时候应该为true
其余的就没什么好说的,一些判断。
继续往下看
if($pwd=='')
{
ResetVdValue();
ShowMsg("密码不能为空!","-1",0,2000);
exit();
}
//检查帐号
$rs = $cfg_ml->CheckUser($userid,$pwd);密码不为空,检查帐号?这是什么意思?我看应该是去数据库中通过用户名和密码去读数据去了,这个真的要跟踪一下了。
此类实例化于/member/config.php中
具体代码
$cfg_ml = new MemberLogin($keeptime);
此类存在于/include/memberlogin.class.php中
这是该类的实例化
function __construct($kptime = -1, $cache=FALSE)
{
global $dsql;
if($kptime==-1){
$this->M_KeepTime = 3600 * 24 * 7;
}else{
$this->M_KeepTime = $kptime;
}
$formcache = FALSE;
$this->M_ID = $this->GetNum(GetCookie("DedeUserID"));
$this->M_LoginTime = GetCookie("DedeLoginTime");
$this->fields = array();
$this->isAdmin = FALSE;
if(empty($this->M_ID))
{
$this->ResetUser();
}else{
$this->M_ID = intval($this->M_ID);
if ($cache)
{
$this->fields = GetCache($this->memberCache, $this->M_ID);
if( empty($this->fields) )
{
$this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
} else {
$formcache = TRUE;
}
} else {
$this->fields = $dsql->GetOne("Select * From `#@__member` where mid='{$this->M_ID}' ");
}
if(is_array($this->fields)){
#api{{
if(defined('UC_API') && @include_once DEDEROOT.'/uc_client/client.php')
{
if($data = uc_get_user($this->fields['userid']))
{
if(uc_check_avatar($data[0]) && !strstr($this->fields['face'],UC_API))
{
$this->fields['face'] = UC_API.'/avatar.php?uid='.$data[0].'&size=middle';
$dsql->ExecuteNoneQuery("UPDATE `#@__member` SET `face`='".$this->fields['face']."' WHERE `mid`='{$this->M_ID}'");
}
}
}
#/aip}}
//间隔一小时更新一次用户登录时间
if(time() - $this->M_LoginTime > 3600)
{
$dsql->ExecuteNoneQuery("update `#@__member` set logintime='".time()."',loginip='".GetIP()."' where mid='".$this->fields['mid']."';");
PutCookie("DedeLoginTime",time(),$this->M_KeepTime);
}
$this->M_LoginID = $this->fields['userid'];
$this->M_MbType = $this->fields['mtype'];
$this->M_Money = $this->fields['money'];
$this->M_UserName = FormatUsername($this->fields['uname']);
$this->M_Scores = $this->fields['scores'];
$this->M_Face = $this->fields['face'];
$this->M_Rank = $this->fields['rank'];
$this->M_Spacesta = $this->fields['spacesta'];
$sql = "Select titles From #@__scores where integral<={$this->fields['scores']} order by integral desc";
$scrow = $dsql->GetOne($sql);
$this->fields['honor'] = $scrow['titles'];
$this->M_Honor = $this->fields['honor'];
if($this->fields['matt']==10) $this->isAdmin = TRUE;
$this->M_UpTime = $this->fields['uptime'];
$this->M_ExpTime = $this->fields['exptime'];
$this->M_JoinTime = MyDate('Y-m-d',$this->fields['jointime']);
if($this->M_Rank>10 && $this->M_UpTime>0){
$this->M_HasDay = $this->Judgemember();
}
if( !$formcache )
{
SetCache($this->memberCache, $this->M_ID, $this->fields, 1800);
}
}else{
$this->ResetUser();
}
}
}上来就缓存一大片,也不管验证有没有通过,这样好吗?这是CheckUser方法
function CheckUser(&$loginuser, $loginpwd)
{
global $dsql;
//检测用户名的合法性
$rs = CheckUserID($loginuser,'用户名',FALSE);
//用户名不正确时返回验证错误,原登录名通过引用返回错误提示信息
if($rs!='ok')
{
$loginuser = $rs;
return '0';
}
//matt=10 是管理员关连的前台帐号,为了安全起见,这个帐号只能从后台登录,不能直接从前台登录
$row = $dsql->GetOne("SELECT mid,matt,pwd,logintime FROM `#@__member` WHERE userid LIKE '$loginuser' ");
if(is_array($row))
{
if($this->GetShortPwd($row['pwd']) != $this->GetEncodePwd($loginpwd))
{
return -1;
}
else
{
//管理员帐号不允许从前台登录
if($row['matt']==10) {
return -2;
}
else {
$this->PutLoginInfo($row['mid'], $row['logintime']);
return 1;
}
}
}
else
{
return 0;
}
}这一句,
SELECT mid,matt,pwd,logintime FROM `#@__member` WHERE userid LIKE '$loginuser'
这是什么意思?不用=号连接,用的是like?逼格深不可测!
其中注意这一句
if($this->GetShortPwd($row['pwd']) != $this->GetEncodePwd($loginpwd))
两个方法 ?
function GetShortPwd($dbpwd)
{
global $cfg_mb_pwdtype;
if(empty($cfg_mb_pwdtype)) $cfg_mb_pwdtype = '32';
$dbpwd = trim($dbpwd);
if(strlen($dbpwd)==16)
{
return $dbpwd;
}
else
{
switch($cfg_mb_pwdtype)
{
case 'l16':
return substr($dbpwd, 0, 16);
case 'r16':
return substr($dbpwd, 16, 16);
case 'm16':
return substr($dbpwd, 8, 16);
default:
return $dbpwd;
}
}
} function GetEncodePwd($pwd)
{
global $cfg_mb_pwdtype;
if(empty($cfg_mb_pwdtype)) $cfg_mb_pwdtype = '32';
switch($cfg_mb_pwdtype)
{
case 'l16':
return substr(md5($pwd), 0, 16);
case 'r16':
return substr(md5($pwd), 16, 16);
case 'm16':
return substr(md5($pwd), 8, 16);
default:
return md5($pwd);
}
}至此,dede登陆机制已经差不多初步完成了。
主要有两种处理,一种是直接md5加密,这个安全性很差。
另外一个是通过对md5的截取,有三种截法。相对安全一些,效率稍微有些折损。
