thinkphp 中单靠 user-agent 白名单无法防爬,因其可伪造;应通过中间件统一过滤 ua,结合行为特征(如 cookie、referer、js 行为)和风险评分动态触发验证码,并强制启用 csrf 令牌防护。

ThinkPHP 里单靠 User-Agent 字符串防爬,根本拦不住真实爬虫——它只适合快速放行可信客户端,或标记明显异常流量(比如空 UA、含 python-requests 却走 Web 路由的请求)。
如何用中间件做 UA 轻量过滤
别在控制器里硬写 if (stripos($ua, 'Chrome') !== false) 这类逻辑。控制器不是流量筛子,复用难、升级难、排查难。正确做法是写一个中间件,比如 app/middleware/UserAgentFilter.php,在 handle() 里统一处理:
- 用
request()->header('user-agent')取值(TP6+),别直接读$_SERVER['HTTP_USER_AGENT'],否则 Swoole/FastCGI 模式下可能为空 - 白名单配置放在
app/extra/spider.php,内容为return ['Mobile Safari', 'Chrome', 'WeChat'];,避免 DB 查询拖慢首字节响应 - 匹配必须用
stripos(),不是==或正则全匹配——合法 UA 变体太多,比如 iPhone 和 iPad 的 UA 仅平台字段不同 - 空字符串、纯数字(如
123456)、或含curl/httpie/python-requests的 UA,应记录日志并限流,不建议直接halt()
为什么 UA 白名单不能当安全屏障
因为 UA 完全可伪造。Scrapy、Playwright、Requests 默认就随机 UA,甚至能完美复刻最新 Chrome UA。你加了白名单,攻击者只要照抄一行字符串就能绕过。
-
curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"就能打穿你的“UA 白名单” - 真正该关注的是:UA 合法但无
Cookie、无Referer、X-Requested-With为空、JS 行为字段缺失(如页面加载到点击间隔小于 800ms) - 检查
request()->cookie('thinkphp_token')是否存在且未过期——真用户有会话,多数爬虫连 Cookie jar 都懒得维护
验证码触发点不能只卡在提交按钮
人机对抗的关键不是“加不加验证码”,而是“在哪个环节、对谁加”。ThinkPHP 自带的 captcha 扩展只提供生成和验证函数,不决定触发时机。
- 首次访问页面时,通过 JS 注入隐藏字段(如
_ts),服务端比对request()->post('_ts')和当前时间差,小于 800ms 的基本是脚本提交 - 对高风险动作(如登录、注册、批量导出)做前置风险评分,而不是固定路径强制弹验证码
- 别把验证码只绑在表单提交按钮上——得前置到页面加载阶段就开始收集行为信号
CSRF 防护必须配合 @csrf 指令
表单防恶意提交,光靠 UA 或 Referer 不顶用。ThinkPHP 的 CSRF 防护依赖令牌机制,必须显式启用:
- 在模板中使用
@csrf指令,它会自动生成<input type="hidden" name="__token__"> - 确保应用已开启框架的 CSRF 中间件(默认 TP6 已启用,但自定义路由或 API 分组可能被绕过)
- POST 请求本身不防 CSRF——攻击者照样能伪造 POST 带上合法 token,所以 token 必须绑定 session + 时间戳 + 随机因子
真正容易被忽略的是:UA 白名单一旦写死,就变成攻击者的路标;而验证码如果只在提交时才校验,等于把门锁装在了门内侧。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











