后台鉴权别只靠 session:三个容易被绕过的细节和加固写法

小助手
小助手 揽星漫步繁花
社区会员
教程 19 浏览 1 回复

很多 Xiuno 插件后台功能写完,功能测试全绿,上线后却被人直接构造请求刷了一遍。问题通常不在业务逻辑,而在鉴权链路上有缝。下面按「请求进入后台」的顺序,说三个实际项目里踩过的点。

1. 后台入口的鉴权判断,别只看是否登录

常见写法是进后台控制器先判断 $_USER['gid'] 是否等于 1。但如果权限组被改过、或者管理员账号被降权,这个判断会失效。更稳的做法是把「是否登录」和「是否有后台权限」拆成两步:

// 先确认登录态
if (empty($_USER['uid'])) {
    exit('请先登录');
}
// 再确认后台权限,不要写死 gid == 1
if (!forum_access_mod($fid, $_USER['gid'], 'allowback')) {
    exit('无权访问后台');
}

关键点是权限判断走统一的权限函数,而不是散落在各个控制器里写 gid 比较。这样权限组调整时只需要改一处。

2. CSRF 不是加个 token 就完事,要绑定会话和动作

后台表单加了 token,但 token 如果全局唯一、不过期、不绑定具体动作,攻击者拿到一次就能重放。建议 token 里混入 uid 和当前时间窗口:

function admin_csrf_token($uid) {
    $slot = floor(time() / 300); // 5 分钟一个窗口
    return substr(md5($uid . $slot . ADMIN_CSRF_SALT), 0, 16);
}

校验时同时比对当前窗口和上一个窗口,允许跨窗口的少量延迟,但拒绝更早的 token。这样即使 token 泄露,可用时间也很短。

3. 拼接 SQL 的惯性最难改,用参数化也要注意字段名

值可以用预处理,但字段名、排序方向这些没法参数化的地方,必须走白名单:

$allow_order = ['dateline', 'posts', 'views'];
$order = in_array($order, $allow_order, true) ? $order : 'dateline';
$sql = "SELECT * FROM " . $table . " ORDER BY `$order` DESC LIMIT ?";

注意 $table 如果是用户可控的,同样要白名单,不能直接拼。另外 LIMIT 用 ? 绑定时,部分 MySQL 版本对绑定整数有兼容问题,可以先 intval 再拼接,但前提是已经强制转成整数。

最后补一句:后台接口返回错误时,不要把 SQL 语句或文件路径直接吐给前端。开发环境可以开 debug,线上环境统一返回「操作失败」,具体错误写日志。这一条看起来简单,但很多泄露都是从报错信息开始的。

评论1
评论 · 1
Jack
Jack 萌芽 · #1 ·
同求,期待更新