后台鉴权别只靠 session:三个容易被绕过的细节和加固写法
很多 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,线上环境统一返回「操作失败」,具体错误写日志。这一条看起来简单,但很多泄露都是从报错信息开始的。