mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 12:45:32 +08:00
54 lines
2.4 KiB
PHP
54 lines
2.4 KiB
PHP
<?php
|
||
|
||
namespace base\admin\middleware;
|
||
|
||
use think\Request;
|
||
|
||
class CsrfMiddlewareBase
|
||
{
|
||
use \app\common\traits\JumpTrait;
|
||
|
||
public function handle(Request $request, \Closure $next)
|
||
{
|
||
if (env('adminsystem.IS_CSRF', true)) {
|
||
if (!in_array($request->method(), ['GET', 'HEAD', 'OPTIONS'])) {
|
||
// MCP 内部分发豁免(唯一豁免通道,安全性论证):
|
||
// 1. mcp_internal 标记只能由服务端写入 login 缓存(McpDispatch 生成的一次性
|
||
// creator token),攻击者需先具备服务器内部写缓存能力才可伪造;
|
||
// 2. 跨站表单/HTML 场景无法携带 Authorization 自定义头(浏览器表单不透传该头),
|
||
// 对该头的 CSRF 攻击模型本身不成立;
|
||
// 3. 豁免标记与 5 分钟一次性 token 同生命周期:McpDispatch 调用结束即删
|
||
// (try/finally),缓存 TTL 到期自动失效,不残留长期豁免通道。
|
||
$authorization = $request->header('authorization', '');
|
||
$token = (strpos($authorization, ' ') !== false) ? trim(explode(' ', $authorization)[1]) : '';
|
||
if (!empty($token)) {
|
||
$cached = \think\facade\Cache::store('login')->get($token);
|
||
if (!empty($cached['mcp_internal'])) {
|
||
return $next($request); // MCP 内部分发标记,跳过 CSRF(一次性短时 token)
|
||
}
|
||
}
|
||
|
||
// 跨域校验
|
||
$refererUrl = $request->header('REFERER', null);
|
||
$refererInfo = parse_url($refererUrl);
|
||
$host = $request->host(true);
|
||
if (!isset($refererInfo['host']) || $refererInfo['host'] != $host) {
|
||
$this->error('当前请求不合法!');
|
||
}
|
||
|
||
// CSRF校验
|
||
// @todo 兼容CK编辑器上传功能
|
||
$ckCsrfToken = $request->post('ckCsrfToken', null);
|
||
$data = !empty($ckCsrfToken) ? ['__token__' => $ckCsrfToken] : [];
|
||
|
||
$check = $request->checkToken('__token__', $data);
|
||
if (!$check) {
|
||
$this->error('请求验证失败,请重新刷新页面!');
|
||
}
|
||
}
|
||
}
|
||
|
||
return $next($request);
|
||
}
|
||
}
|