You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Origin头的跨域请求验证方案的安全性及Origin伪造问题咨询

基于Origin头的跨域请求验证方案的安全性及Origin伪造问题咨询

嘿,我来帮你拆解这个问题~先看你写的这段中间件代码:

public function handle($request, Closure $next) { 
    $requestHost = parse_url($request->headers->get('origin'), PHP_URL_HOST); 
    if ($requestHost != env('APP_URL')) { 
        return response()->json('Wrong Origin Mate', 200); 
    } else { 
        return $next($request); 
    } 
}

先回答你最关心的两个核心问题:

一、Origin头能不能被伪造?

分两种场景说清楚:

  • 浏览器发起的跨域请求:这个Origin头是浏览器自动添加的,而且完全无法通过前端JS代码篡改——浏览器的同源策略会强制管控这个值,所以从合法浏览器请求过来的Origin是100%真实的,这部分你可以放心。
  • 非浏览器发起的请求:比如用curl、Postman、或者其他后端服务直接调用的话,Origin头确实可以随便伪造,但这类请求本来就不属于你要防的“前端跨域非法请求”场景。如果你的接口是给自家前端页面用的,核心防护目标就是浏览器端的跨域攻击,那Origin的可信度足够。

二、你当前方案的隐患

你的思路方向是对的,但代码里有几个容易踩的坑:

  • 对比逻辑的不匹配env('APP_URL')一般是带协议的完整地址(比如https://myapp.com),但你用PHP_URL_HOST取到的是不带协议的主机名(比如myapp.com),直接对比的话会永远不相等,导致所有合法请求都被拦截,这是典型的逻辑bug。
  • Origin头不存在的情况:同域请求的时候,浏览器大概率不会发送Origin头,这时候$request->headers->get('origin')会返回null,parse_url处理null会报错,或者得到null,直接触发错误返回,把合法的同域请求也拦了。
  • 状态码语义错误:你返回的是200(成功状态码),但这是非法请求,应该用403 Forbidden,不管是给前端还是日志排查,语义都更准确。

三、改进后的代码示例

给你调整一下代码,解决上面的问题:

public function handle($request, Closure $next) {
    $origin = $request->headers->get('origin');
    
    // 同域请求无Origin头,直接放行
    if (!$origin) {
        return $next($request);
    }
    
    // 解析请求Origin的主机名
    $requestHost = parse_url($origin, PHP_URL_HOST);
    // 提前从配置解析允许的主机名(建议在config/app.php里预定义,不要每次解析env)
    $allowedHost = parse_url(config('app.url'), PHP_URL_HOST);
    
    // 统一转小写避免大小写匹配问题
    if (strtolower($requestHost) !== strtolower($allowedHost)) {
        return response()->json('Wrong Origin Mate', 403);
    }
    
    return $next($request);
}

最后再提个安全小提醒

如果你的接口除了给前端用,还要接受其他后端服务的调用,那只靠Origin验证是不够的——因为后端请求可以随便伪造Origin。这时候你需要额外的验证手段,比如API密钥、请求签名之类的,来确保请求的合法性。

内容来源于stack exchange

火山引擎 最新活动