Yii 2.0.46 无跨域漏洞,问题源于 CORS 配置不当:禁止 wildcard Origin 与 credentials 共存;需指定白名单域名、显式声明 headers、确保 OPTIONS 路由可达,并在认证失败前手动注入 CORS 头。

Yii 2.0.46 本身没有被官方标记为存在“跨域漏洞”,所谓“跨域问题”其实是配置不当引发的安全响应错误或功能失效,核心在于 CORS 策略与凭证(credentials)的冲突配置,不是框架代码级漏洞。修复关键不是打补丁,而是正确配置响应头和请求生命周期控制。
必须避开 wildcard + credentials 冲突
浏览器明确禁止:Access-Control-Allow-Origin: * 与 Access-Control-Allow-Credentials: true 同时存在。
Yii 2.0.46 的 yii\filters\Cors 在检测到该组合时会直接抛出异常(如 Allowing credentials for wildcard origins is insecure),导致预检失败或接口不可用。
正确做法:
- Origin 必须指定具体域名数组,不能写
['*'] - 若前端用了
withCredentials: true(如 axios 默认携带 cookie 或手动传 token),后端必须匹配精确来源
'corsFilter' => [
'class' => \yii\filters\Cors::class,
'cors' => [
'Origin' => ['https://admin.example.com', 'http://localhost:8080'],
'Access-Control-Allow-Credentials' => true,
'Access-Control-Allow-Headers' => ['Authorization', 'Content-Type', 'X-Requested-With'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
'Access-Control-Max-Age' => 3600,
],
],⚠️ 注意:
Access-Control-Allow-Headers不要写['*']—— Yii 2.0.46 对通配符支持不完整,建议显式列出前端实际发送的头(如Authorization)。
OPTIONS 预检请求必须路由可达
浏览器发预检请求(OPTIONS 方法)时,若 Yii 没有对应路由规则,会返回 405 Method Not Allowed。
解决方式:
在 URL 管理配置(如 config/web.php 的 urlManager.rules)中,为 API 路径显式添加 OPTIONS 支持:
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'v1/user',
'extraPatterns' => [
'OPTIONS <id:\d+>' => 'options',
'OPTIONS' => 'options',
],
],
]或者在控制器行为中统一加:
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['corsFilter'] = [/* 上面的配置 */];
return $behaviors;
}认证失败(401/403)时 CORS 头丢失
这是最隐蔽的问题:当 HttpBearerAuth 或 QueryParamAuth 验证失败抛出 UnauthorizedHttpException,CorsFilter 还没执行,响应里就没有 CORS 头 → 浏览器报错 No 'Access-Control-Allow-Origin' header is present。
修复逻辑:
让响应头在认证环节就写入,而不是等过滤器链走到 Cors。
推荐做法:重写认证类,在 authenticate() 中手动设头:
// common/components/SecureHttpBearerAuth.php
namespace common\components;
use yii\filters\auth\HttpBearerAuth;
use yii\web\Response;
class SecureHttpBearerAuth extends HttpBearerAuth
{
public function authenticate($user, $request, $response)
{
$identity = parent::authenticate($user, $request, $response);
if ($identity === null) {
$origin = $request->getHeaders()->get('Origin');
// 只允许白名单 Origin 才写头(防滥用)
$allowedOrigins = ['https://admin.example.com', 'http://localhost:8080'];
if (in_array($origin, $allowedOrigins)) {
$response->getHeaders()
->set('Access-Control-Allow-Origin', $origin)
->set('Access-Control-Allow-Credentials', 'true')
->set('Access-Control-Allow-Headers', 'Authorization, Content-Type');
}
}
return $identity;
}
}然后在控制器中使用它:
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'authenticator' => [
'class' => SecureHttpBearerAuth::class,
],
]);
}不要在 Apache/Nginx 层硬加 CORS 头
虽然简单,但会导致:
- 无法动态判断 Origin(比如生产环境只允许可信域名)
- 无法结合登录态做细粒度控制(如“仅登录用户才允许某前端域名”)
- 与 Yii 层头重复或覆盖,浏览器只取最后一个,行为不可控
如确需 Web 服务器层辅助(例如静态资源),请确保:
- 仅对
/api/*路径生效 - 不设置
Access-Control-Allow-Credentials(交给 PHP 控制) - 关闭
Access-Control-Allow-Origin: *全局配置
不复杂但容易忽略


















