XIUNOX 账号安全设置流程优化-使用教程-XIUNOX [复制链接]

一级用户组
金小颖论坛 AI 摘要
XIUNOX 优化账号安全设置:修改邮箱通过当前密码与新邮箱验证码双重验证,并通知旧邮箱;修改密码需验证旧密码、校验强度、发送通知并强制重新登录。系统采用四层机制防止重复提交,为 OAuth 无密码用户提供验证码和旧邮箱通知保障。邮件变量须转义防注入,Web 与 API 端保持相同安全逻辑。
本文共计140个字,预计阅读时长0.4分钟。

修改邮箱流程

修改邮箱属于账号敏感操作,采用"当前密码 + 新邮箱验证码"双重验证机制:

用户提交修改邮箱请求
    ↓
┌─────────────────────────────────────┐
│ 步骤 1:密码验证(仅对有密码用户)    │
│   - 验证当前登录密码                  │
│   - 防止会话劫持后直接改绑邮箱         │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 步骤 2:验证码验证                    │
│   - 新邮箱必须通过验证码验证           │
│   - 格式校验 + 域名白名单检查          │
│   - 唯一性检查(邮箱未被他人使用)     │
│   - 频率限制(防止短信轰炸)           │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 步骤 3:执行修改                      │
│   - 更新 user 表 email 字段           │
│   - 发送通知邮件到旧邮箱               │
└─────────────────────────────────────┘
    ↓
跳转回安全设置页,显示新邮箱

关键设计点

  1. 密码验证条件判断

// 仅对有密码用户要求验证当前密码
$has_password = !empty($user['password']) || !empty($user['password_hash']);
if ($has_password) {
    // 验证当前密码
    $verified = user_login_verify($password_current, $user);
    if (!$verified) {
        message(-1, lang('password_incorrect'));
    }
}
// 无密码用户(纯 OAuth 绑定账号)由新邮箱验证码 + 旧邮箱通知兜底
  1. 旧邮箱通知机制

// 修改成功后立即通知旧邮箱
$old_email = $user['email'];
$template = xn_email_template('email_change_notify', array(
    'sitename' => $conf['sitename'],
    'username' => esc_html($user['username']),
    'old_email' => $old_email,
    'new_email' => $email_new,
    'time' => date('Y-m-d H:i:s', $time),
));
xn_send_mail($smtp, $conf['sitename'], $old_email, $template['subject'], $template['body']);

旧邮箱通知的安全意义

  • 防撞库后静默改绑:如果攻击者通过撞库获取密码,修改邮箱后旧邮箱会收到通知

  • 用户可第一时间察觉:即使账号被劫持,原主人能通过通知邮件发现异常

  • 成本极低:仅增加一封邮件,无额外安全成本

修改密码流程

修改密码采用"旧密码验证 + 新密码强度校验 + 通知邮件"机制:

用户提交修改密码请求
    ↓
┌─────────────────────────────────────┐
│ 步骤 1:旧密码验证                    │
│   - 验证当前密码是否正确               │
│   - 防止会话被劫持后直接修改密码       │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 步骤 2:新密码校验                    │
│   - 两次输入一致性检查                 │
│   - 密码强度策略校验                   │
│     · 最小长度(默认 6 位)            │
│     - 复杂度要求(数字/大小写/特殊字符)│
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│ 步骤 3:执行修改                      │
│   - 更新密码哈希                      │
│   - 清除 Session Token(强制重新登录) │
│   - 发送通知邮件到当前邮箱             │
└─────────────────────────────────────┘
    ↓
跳转到登录页,要求用户重新登录

关键设计点

  1. 强制重新登录

// 修改成功后清除 token,强制用户重新登录
user_token_clear($uid);
message(0, lang('password_modify_successfully'), array('redirect_url' => url('user-login')));
  1. 安全通知邮件

// 通知邮件使用 password_change_notify 模板
$template = xn_email_template('password_change_notify', array(
    'sitename' => $conf['sitename'],
    'username' => esc_html($user['username']),
    'time' => date('Y-m-d H:i:s', $time),
));

强制重新登录的安全意义

  • 使所有现有会话失效,防止攻击者利用旧会话

  • 用户需用新密码重新登录,确认修改是本人操作

  • 避免"改密后攻击者仍在操作"的风险

表单防重复提交

账号安全设置页面采用统一的防重复提交机制 xnx_form_guard()

function xnx_form_guard(formId, btnId) {
    var form = document.getElementById(formId);
    var btn = document.getElementById(btnId);
    var busy = false;
    var originalText = btn.textContent;

    // 1. submit capture 阶段同步拦截(最快响应)
    form.addEventListener('submit', function(evt) {
        if (busy) {
            evt.preventDefault();
            evt.stopPropagation();
            return;
        }
        busy = true;
        btn.disabled = true;
        btn.textContent = btn.getAttribute('data-loading-text') || '提交中...';
    }, true);

    // 2. 请求期间保持禁用状态
    form.addEventListener('htmx:before:request', function() {
        // 设置 30 秒超时兜底
        setTimeout(restore, 30000);
    });

    // 3. 请求完成后恢复(无论成功/失败)
    form.addEventListener('htmx:after:request', restore);

    // 4. 网络错误立即恢复
    form.addEventListener('htmx:response:error', function() {
        restore();
        XN.toast('网络请求失败,请重试', 'danger');
    });

    function restore() {
        busy = false;
        btn.disabled = false;
        btn.textContent = originalText;
    }
}

防重机制的四层保障

层级

时机

作用

1

submit capture 阶段

同步拦截快速双击,防止重复提交

2

htmx:before:request

请求期间禁用按钮,显示 loading 状态

3

htmx:after:request

请求完成后立即恢复按钮状态

4

htmx:response:error / 30s 超时

网络错误或超时时兜底恢复

OAuth 无密码用户的特殊处理

纯 OAuth 绑定账号(无密码)在修改邮箱时采用不同的安全策略:

// 判断用户是否有密码
$has_password = !empty($user['password']) || !empty($user['password_hash']);

if ($has_password) {
    // 有密码:当前密码 + 新邮箱验证码
    // 双重验证,防止会话劫持
} else {
    // 无密码:新邮箱验证码 + 旧邮箱通知
    // 验证码验证新邮箱归属
    // 旧邮箱通知用户修改行为
}

无密码用户的安全兜底

  • 验证码验证:确保新邮箱归当前用户所有

  • 旧邮箱通知:修改成功后立即通知旧邮箱,用户可第一时间察觉异常

  • 无密码账号补全:登录时强制要求设置密码(见第三章)

邮件模板说明

系统使用以下邮件模板保障账号安全:

模板键

用途

触发时机

email_change_code

邮箱验证码

发送新邮箱验证码时

email_change_notify

改邮箱通知

修改邮箱成功后,发送到旧邮箱

password_change_notify

改密通知

修改密码成功后,发送到当前邮箱

模板变量

// email_change_code
array('code' => $code, 'sitename' => $sitename)

// email_change_notify
array(
    'sitename' => $sitename,
    'username' => $username,      // 已 esc_html 转义
    'old_email' => $old_email,
    'new_email' => $new_email,
    'time' => $modify_time
)

// password_change_notify
array(
    'sitename' => $sitename,
    'username' => $username,      // 已 esc_html 转义
    'time' => $modify_time
)

安全提示:邮件模板中的 username 变量必须使用 esc_html() 转义,防止 HTML 注入攻击。

API 端同步

Web 端和 API 端的安全逻辑保持一致:

端点

方法

说明

my-security.html

GET/POST

Web 端安全设置页

api/v1/my.php

PUT

API 端修改邮箱/密码

API 端同样执行:

  • 当前密码验证(有密码用户)

  • 邮箱验证码验证

  • 密码强度校验

  • 旧邮箱/改密通知邮件

最新回复

请先登录后再回复 登录

uid:2 一级用户组
关注
发帖 1363
评论 0
粉丝 0
关注 0
发新帖
目录
XIUNOX 账号安全设置流程优化-使用教程-XIUNOX