后台登录接口被扫了三千次后,我才把鉴权这堂课补完

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
站长杂谈 59 浏览 0 回复

上周服务器告警,某个子站的后台登录接口一夜之间被 POST 了三千多次。IP 分散在东南亚各地,payload 里全是 admin/123456、admin/admin888 这种经典组合。虽然都没撞对,但看着日志里密密麻麻的 401,我后背还是凉了一下——这要是哪次手滑把测试账号的弱密码忘了删呢?

趁周末把几个项目的后台安全逻辑彻底捋了一遍,发现之前写的鉴权简直是"防君子不防小人"。这里记几个实打实踩过的坑,给同样觉得"后台嘛,加个登录框就够了"的兄弟提个醒。

一、Session 和 Token 混着用,等于给攻击者留了两扇门

老项目用的是传统 Session,新项目图省事上了 JWT。结果有次把新项目的接口层代码复制到老项目里,鉴权逻辑没改干净——前端带着 JWT 头来请求,后端居然先用 Session 判了一遍,Session 过期了又 fallback 去验 JWT。攻击者拿着一个过期的 JWT,在某些接口上居然能蒙混过关。后来统一了鉴权中间件,要么全 Session 要么全 Token,禁止"双语种"并存。

二、CSRF 防护别只盯着表单,AJAX 接口才是重灾区

早期只在传统表单提交里加了 CSRF token,AJAX 接口觉得"反正有 CORS 限制"就没管。直到有次测试,用 Postman 关掉 CORS 校验直接调用后台的"修改管理员邮箱"接口——好家伙,带着用户已登录的 Cookie,跨站请求畅通无阻。现在我的做法是:所有写操作接口统一校验双重凭证,Cookie 里的 Session 管"你是谁",Header 里的 X-CSRF-Token 管"你是不是故意的",缺一不可。

ThinkPHP 里有个细节,TP6 的表单令牌默认只验证 POST,如果你的删除操作为了"RESTful"用了 DELETE 方法,框架默认不校验 CSRF,得手写中间件补上。

三、SQL 注入的"漏网之鱼"往往在排序和分组里

参数化查询防住了 WHERE 条件,结果翻车在 ORDER BY。后台列表页支持点击表头排序,前端传个字段名过来,后端直接拼接:`order($sortField . ' ' . $sortOrder)`。当时觉得字段名是白名单控制的,应该没事——直到白名单里有个 `create_time`,而攻击者传了 `create_time;DROP TABLE admin--`。PDO 参数化绑不了字段名和排序方向,现在一律改成硬编码映射:`$allowed = ['id'=>'id', 'time'=>'create_time']`,前端传 key,后端查字典,多一步能救命。

同理,GROUP BY、LIMIT 后面的页码和条数,也别直接拼数字,intval 一下花不了几毫秒。

四、权限校验别只在菜单层做,接口层必须"二次确认"

曾经以为后台菜单按角色隐藏了,用户就看不到没权限的功能。直到有人直接 F12 抄了接口地址,或者用 Postman 重放历史请求——菜单是没了,接口可还在那儿裸奔。现在的规矩是:每个需要鉴权的接口,入口第一件事就是查当前用户的权限节点,菜单显示和接口放行各自独立校验。ThinkPHP 里可以封装一个 `Auth::check('node_name')`,Controller 里一行调用,比事后补漏洞省心得多。

五、登录失败别给提示,日志里要留痕

以前登录错误返回"用户名不存在"或"密码错误",方便用户排查。现在统一返回"账号或密码错误",但后台日志里详细记录:是用户名不存在、密码错、还是账号被锁定。既防枚举,又方便自己溯源。配合 fail2ban 或者宝塔的 Nginx 防火墙,同一个 IP 十分钟内五次错误直接拉黑,比什么前端验证码都管用。

——

补完这些课再回头看,那三千次扫描其实是个便宜学费。真要被人用弱密码或者接口漏洞摸进来,删库都是轻的,挂个菠菜跳转才是恶心到吐。安全这东西没有"差不多行了",每个口子都得假设"外面有人正在试"。

各位站长兄弟,你们后台的登录日志最近看过吗?建议现在就去瞄一眼,说不定有"惊喜"。

评论0
回复 · 0
还没有回复
微信客服 微信客服