Browse Source

增强后台安全限制

优化登录判断机制
移除冗余代码

(cherry picked from commit 7e6314a701516ee50aa00dc3be0e639ddd664057)
Karson 1 year ago
parent
commit
57c2f23106

+ 4 - 1
application/admin/controller/Index.php

@@ -70,6 +70,8 @@ class Index extends Backend
         if ($this->auth->isLogin()) {
             $this->success(__("You've logged in, do not login again"), $url);
         }
+        //保持会话有效时长,单位:小时
+        $keeyloginhours = 24;
         if ($this->request->isPost()) {
             $username = $this->request->post('username');
             $password = $this->request->post('password');
@@ -95,7 +97,7 @@ class Index extends Backend
                 $this->error($validate->getError(), $url, ['token' => $this->request->token()]);
             }
             AdminLog::setTitle(__('Login'));
-            $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0);
+            $result = $this->auth->login($username, $password, $keeplogin ? $keeyloginhours * 3600 : 0);
             if ($result === true) {
                 Hook::listen("admin_login_after", $this->request);
                 $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]);
@@ -113,6 +115,7 @@ class Index extends Backend
         }
         $background = Config::get('fastadmin.login_background');
         $background = $background ? (stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background) : '';
+        $this->view->assign('keeyloginhours', $keeyloginhours);
         $this->view->assign('background', $background);
         $this->view->assign('title', __('Login'));
         Hook::listen("admin_login_init", $this->request);

+ 3 - 3
application/admin/controller/auth/Admin.php

@@ -129,7 +129,7 @@ class Admin extends Backend
                         exception(__("Please input correct password"));
                     }
                     $params['salt'] = Random::alnum();
-                    $params['password'] = md5(md5($params['password']) . $params['salt']);
+                    $params['password'] = $this->auth->getEncryptPassword($params['password'], $params['salt']);
                     $params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。
                     $result = $this->model->validate('Admin.add')->save($params);
                     if ($result === false) {
@@ -183,7 +183,7 @@ class Admin extends Backend
                             exception(__("Please input correct password"));
                         }
                         $params['salt'] = Random::alnum();
-                        $params['password'] = md5(md5($params['password']) . $params['salt']);
+                        $params['password'] = $this->auth->getEncryptPassword($params['password'], $params['salt']);
                     } else {
                         unset($params['password'], $params['salt']);
                     }
@@ -192,7 +192,7 @@ class Admin extends Backend
                     $adminValidate->rule([
                         'username' => 'require|regex:\w{3,30}|unique:admin,username,' . $row->id,
                         'email'    => 'require|email|unique:admin,email,' . $row->id,
-                        'mobile'    => 'regex:1[3-9]\d{9}|unique:admin,mobile,' . $row->id,
+                        'mobile'   => 'regex:1[3-9]\d{9}|unique:admin,mobile,' . $row->id,
                         'password' => 'regex:\S{32}',
                     ]);
                     $result = $row->validate('Admin.edit')->save($params);

+ 1 - 0
application/admin/lang/zh-cn/index.php

@@ -65,5 +65,6 @@ return [
     'Forum'                                                      => '交流社区',
     'QQ qun'                                                     => 'QQ交流群',
     'Captcha'                                                    => '验证码',
+    'The duration of the session is %s hours'                    => '设定会话有效时长为%s小时',
     'Security tips'                                              => '<i class="fa fa-warning"></i> 安全提示:为了你的后台安全,请勿将后台管理入口设置为admin或admin.php',
 ];

+ 67 - 13
application/admin/library/Auth.php

@@ -51,7 +51,7 @@ class Auth extends \fast\Auth
             $this->setError('Please try again after 1 day');
             return false;
         }
-        if ($admin->password != md5(md5($password) . $admin->salt)) {
+        if ($admin->password != $this->getEncryptPassword($password, $admin->salt)) {
             $admin->loginfailure++;
             $admin->save();
             $this->setError('Password is incorrect');
@@ -63,7 +63,8 @@ class Auth extends \fast\Auth
         $admin->token = Random::uuid();
         $admin->save();
         Session::set("admin", $admin->toArray());
-        $this->keeplogin($keeptime);
+        Session::set("admin.safecode", $this->getEncryptSafecode($admin));
+        $this->keeplogin($admin, $keeptime);
         return true;
     }
 
@@ -101,7 +102,7 @@ class Auth extends \fast\Auth
                 return false;
             }
             //token有变更
-            if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token . config('token.key'))) {
+            if ($key != $this->getKeeploginKey($admin, $keeptime, $expiretime)) {
                 return false;
             }
             $ip = request()->ip();
@@ -110,8 +111,9 @@ class Auth extends \fast\Auth
                 return false;
             }
             Session::set("admin", $admin->toArray());
+            Session::set("admin.safecode", $this->getEncryptSafecode($admin));
             //刷新自动登录的时效
-            $this->keeplogin($keeptime);
+            $this->keeplogin($admin, $keeptime);
             return true;
         } else {
             return false;
@@ -124,18 +126,64 @@ class Auth extends \fast\Auth
      * @param int $keeptime
      * @return  boolean
      */
-    protected function keeplogin($keeptime = 0)
+    protected function keeplogin($admin, $keeptime = 0)
     {
         if ($keeptime) {
             $expiretime = time() + $keeptime;
-            $key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token . config('token.key'));
-            $data = [$this->id, $keeptime, $expiretime, $key];
-            Cookie::set('keeplogin', implode('|', $data), 86400 * 7);
+            $key = $this->getKeeploginKey($admin, $keeptime, $expiretime);
+            Cookie::set('keeplogin', implode('|', [$admin['id'], $keeptime, $expiretime, $key]), $keeptime);
             return true;
         }
         return false;
     }
 
+    /**
+     * 获取密码加密后的字符串
+     * @param string $password 密码
+     * @param string $salt     密码盐
+     * @return string
+     */
+    public function getEncryptPassword($password, $salt = '')
+    {
+        return md5(md5($password) . $salt);
+    }
+
+    /**
+     * 获取密码加密后的自动登录码
+     * @param string $password 密码
+     * @param string $salt     密码盐
+     * @return string
+     */
+    public function getEncryptKeeplogin($params, $keeptime)
+    {
+        $expiretime = time() + $keeptime;
+        $key = md5(md5($params['id']) . md5($keeptime) . md5($expiretime) . $params['token'] . config('token.key'));
+        return implode('|', [$this->id, $keeptime, $expiretime, $key]);
+    }
+
+    /**
+     * 获取自动登录Key
+     * @param $params
+     * @param $keeptime
+     * @param $expiretime
+     * @return string
+     */
+    public function getKeeploginKey($params, $keeptime, $expiretime)
+    {
+        $key = md5(md5($params['id']) . md5($keeptime) . md5($expiretime) . $params['token'] . config('token.key'));
+        return $key;
+    }
+
+    /**
+     * 获取加密后的安全码
+     * @param $params
+     * @return string
+     */
+    public function getEncryptSafecode($params)
+    {
+        return md5(md5($params['username']) . md5(substr($params['password'], 0, 6)) . config('token.key'));
+    }
+
     public function check($name, $uid = '', $relation = 'or', $mode = 'url')
     {
         $uid = $uid ? $uid : $this->id;
@@ -180,13 +228,19 @@ class Auth extends \fast\Auth
         if (!$admin) {
             return false;
         }
+        $my = Admin::get($admin['id']);
+        if (!$my) {
+            return false;
+        }
+        //校验安全码,可用于判断关键信息发生了变更需要重新登录
+        if (!isset($admin['safecode']) || $this->getEncryptSafecode($my) !== $admin['safecode']) {
+            $this->logout();
+            return false;
+        }
         //判断是否同一时间同一账号只能在一个地方登录
         if (Config::get('fastadmin.login_unique')) {
-            $my = Admin::get($admin['id']);
-            if (!$my || $my['token'] != $admin['token']) {
-                $this->logined = false; //重置登录状态
-                Session::delete("admin");
-                Cookie::delete("keeplogin");
+            if ($my['token'] != $admin['token']) {
+                $this->logout();
                 return false;
             }
         }

+ 12 - 14
application/admin/model/Admin.php

@@ -13,22 +13,20 @@ class Admin extends Model
     // 定义时间戳字段名
     protected $createTime = 'createtime';
     protected $updateTime = 'updatetime';
+    protected $hidden = [
+        'password',
+        'salt'
+    ];
 
-    /**
-     * 重置用户密码
-     * @author baiyouwen
-     */
-    public function resetPassword($uid, $NewPassword)
+    public static function init()
     {
-        $passwd = $this->encryptPassword($NewPassword);
-        $ret = $this->where(['id' => $uid])->update(['password' => $passwd]);
-        return $ret;
-    }
-
-    // 密码加密
-    protected function encryptPassword($password, $salt = '', $encrypt = 'md5')
-    {
-        return $encrypt($password . $salt);
+        self::beforeWrite(function ($row) {
+            $changed = $row->getChangedData();
+            //如果修改了用户或或密码则需要重新登录
+            if (isset($changed['username']) || isset($changed['password']) || isset($changed['salt'])) {
+                $row->token = '';
+            }
+        });
     }
 
 }

+ 5 - 2
application/admin/view/index/login.html

@@ -28,7 +28,7 @@
             box-shadow: 0 0 30px rgba(0, 0, 0, 0.1);
             background: rgba(255, 255, 255, 1);
             border: none;
-            overflow: hidden;
+            /*overflow: hidden;*/
             padding: 0;
         }
 
@@ -55,6 +55,7 @@
 
         .login-head {
             background: #899fe1;
+            border-radius: 3px 3px 0 0;
         }
 
         .login-form {
@@ -122,12 +123,14 @@
                         </div>
                         {/if}
                         <!--@CaptchaEnd-->
+                        {if $keeyloginhours>0}
                         <div class="form-group checkbox">
-                            <label class="inline" for="keeplogin">
+                            <label class="inline" for="keeplogin" data-toggle="tooltip" title="{:__('The duration of the session is %s hours', $keeyloginhours)}">
                                 <input type="checkbox" name="keeplogin" id="keeplogin" value="1"/>
                                 {:__('Keep login')}
                             </label>
                         </div>
+                        {/if}
                         <div class="form-group">
                             <button type="submit" class="btn btn-success btn-lg btn-block" style="background:#708eea;">{:__('Sign in')}</button>
                         </div>