直接设为*危险,因允许任意域名读取响应且无法携带credentials;需动态校验Origin并精确匹配白名单,仅放行必要methods和headers,确保header前置输出且不重复。

PHP中header('Access-Control-Allow-Origin: *')为什么危险
直接设为*会允许任意域名读取响应内容,配合credentials(如 Cookie、Authorization 头)时浏览器会直接拒绝——但更严重的是,它让前端可无限制调用你的 API,暴露敏感接口或触发未授权操作。
常见误用场景:开发环境随手加了这行,上线后没改;用框架默认中间件但没约束来源;JSONP 时代遗留的“兼容写法”被复制到现代 CORS 配置中。
- 若需携带 Cookie,请务必指定具体域名,不能用
*,且必须同时设置Access-Control-Allow-Credentials: true -
Access-Control-Allow-Origin和Access-Control-Allow-Credentials必须成对出现,否则浏览器报错The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when the request's credentials mode is 'include' - 动态白名单比静态
*多几行代码,但能拦截 99% 的恶意跨域探测
如何安全地动态校验并设置Access-Control-Allow-Origin
核心是:从Origin请求头中提取协议+域名,与预设白名单比对,匹配则回写该值,不匹配则不输出该 header(或返回空/403)。
示例逻辑(不依赖框架):
立即学习“PHP免费学习笔记(深入)”;
$allowed_origins = ['https://a.example.com', 'https://b.example.net'];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowed_origins)) {
header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');
}
注意点:
- 不要用
parse_url($origin)['host']做模糊匹配(如只比 host),攻击者可构造https://evil.com@your-api.com绕过 - 确保
$origin非空且为合法 URL 格式,否则跳过设置(避免空 Origin 被当成*) - 如果用 Nginx 做反向代理,确认
Origin头未被覆盖或丢失(检查proxy_pass_request_headers on)
Access-Control-Allow-Methods和Access-Control-Allow-Headers别盲目放开
很多项目写成GET, POST, PUT, DELETE, OPTIONS全放开,或直接用*,等于告诉攻击者“我支持所有动词和自定义头”,增大 SSRF、CSRF 或服务端请求伪造风险。
真实建议:
- 只列出当前接口真正需要的 method,比如只读接口只需
GET, HEAD, OPTIONS -
Access-Control-Allow-Headers应明确写出前端实际发送的头,如Content-Type, X-Requested-With, Authorization;不要写*,尤其避免放行Cookie或Authorization却不配Allow-Credentials - 预检请求(OPTIONS)必须返回正确 headers,否则浏览器会直接阻断后续请求,调试时可用
curl -I -X OPTIONS -H "Origin: https://a.example.com" your-api.com/endpoint验证
PHP 中启用 CORS 时容易忽略的底层细节
CORS 是浏览器行为,PHP 本身不“处理”跨域,只是输出响应头。这意味着:错误的 header 顺序、重复输出、或在输出后才写 header,都会导致失效或安全策略被忽略。
- 确保
header()调用在任何echo、print、空白符或 HTML 输出之前,否则报Cannot modify header information - 不要在多个地方重复设置同一 header(如框架中间件 + 手动
header()),可能造成冲突或覆盖 - CLI 模式下
$_SERVER['HTTP_ORIGIN']不存在,需提前判断,否则in_array()会警告 - 如果用 Composer 加载类或路由,CORS 头最好放在最外层入口(如
public/index.php)或统一中间件,避免每个控制器都手写一遍
最易被绕过的不是配置多复杂,而是把Origin当信任输入直接回写——哪怕白名单校验再严,一旦拼接进 header 前没过滤换行符,就可能注入额外 header。











