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的截取,有三种截法。相对安全一些,效率稍微有些折损。

2014-10-09 12:24:58

php
php

这是介绍的地方

本文相关标签

推荐应用

友情链接


皖ICP备14007051号-2 关于穆子龙