首页 / 帮助文档 / 网站安全之CORS跨域策略严格配置避免信息泄露

网站安全之CORS跨域策略严格配置避免信息泄露

很多网站在配置CORS时图省事,直接设置"Access-Control-Allow-Origin: *",这相当于在自家银行金库门口挂了个牌子:“允许所有人进出”。一旦你的网站需要携带用户凭证(如Cookies、Authorization头)发起跨域请求,这种宽松策略不仅无效,更危险的是,它暴露了你并未对哪些外部域可以访问你的资源进行任何思考。信息泄露、CSRF攻击、内部API被恶意站点滥用,风险随之而来。正确的做法是,根据业务需求,严格、精确地配置允许的来源、方法和头部,并充分利用预检请求(Preflight Request)机制进行防护。

CORS的核心机制与安全风险

CORS(跨源资源共享)本身是一套安全机制,用于在浏览器端安全地决定是否允许跨域请求。其核心是服务器通过一系列以"Access-Control-"开头的HTTP响应头来声明策略。风险正源于配置不当:将"Access-Control-Allow-Origin"设置为通配符"*",意味着任何外部网站都可以通过前端JavaScript代码读取你站点资源的响应内容。如果你的API返回了敏感用户数据、内部业务逻辑信息,这些数据就会被轻易窃取。此外,宽松的"Access-Control-Allow-Methods"(如允许PUT、DELETE)和"Access-Control-Allow-Headers"可能被攻击者利用,与其他漏洞结合实施攻击。

严格配置:从通配符到精确白名单

摒弃通配符"*"是第一步。你应该建立一个明确、尽可能窄的源(Origin)白名单。动态检查请求中的"Origin"头部,并与白名单比对,匹配成功后再将其值作为"Access-Control-Allow-Origin"头的值返回。这确保了只有受信任的域才能访问资源。绝对不要简单地将请求的"Origin"值直接反射回去而不加验证,那将完全失去控制。

// Node.js/Express 示例
const allowedOrigins = ['https://www.yourdomain.com', 'https://trusted-partner.com'];
app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  // 重要:对于需要凭证的请求,不能使用 '*'
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  next();
});

精细化控制方法与请求头

不要默认允许所有HTTP方法。只开放业务实际需要的方法,例如GET、POST。对于可能对资源产生副作用的PUT、PATCH、DELETE等方法,更要严格控制。"Access-Control-Allow-Headers"也应如此,明确列出前端请求中被允许的自定义或特殊头部(如"Authorization", "X-API-Key"),避免攻击者注入恶意头部。

// 更完整的CORS中间件配置示例
app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
    res.header('Access-Control-Allow-Origin', origin);
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH');
    res.header('Access-Control-Allow-Headers', 'Authorization, Content-Type, X-Requested-With');
  }
  // 处理预检请求
  if (req.method === 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

理解并利用预检请求(Preflight)

对于“非简单请求”,浏览器会先发送一个OPTIONS方法的预检请求。这是关键的安全关卡。服务器必须在预检请求的响应中明确批准即将到来的真实请求的方法和头。通过严格响应预检请求,你可以拦截掉那些试图使用非常规方法或头部的恶意跨域请求。确保你的服务器正确处理OPTIONS请求,并返回与对应跨域请求一致的严格策略。

携带凭证时的特殊配置

当你的跨域请求需要携带Cookies或HTTP认证信息("withCredentials: true")时,安全要求更为严格。此时,"Access-Control-Allow-Origin"不能为通配符"*",必须是明确的、单一的源。同时,必须设置"Access-Control-Allow-Credentials: true"。这形成了一个“闭环”验证:只允许某个特定域,且该域被允许携带凭证访问,极大提升了安全性。

暴露响应头与缓存策略的风险控制

"Access-Control-Expose-Headers"头用于控制哪些响应头可以暴露给前端JavaScript。默认只暴露简单响应头(Cache-Control、Content-Language等)。如果你需要暴露自定义头(如"X-Total-Count"),应明确列出,避免无意中泄露"Server"、"X-Powered-By"等服务器敏感信息。此外,"Access-Control-Max-Age"用于缓存预检请求结果,设置过长的时间会降低策略调整的灵活性,建议根据业务变更频率设置合理值。

服务器端验证与错误处理

CORS头是浏览器的“交通指示牌”,恶意攻击者完全可以绕过浏览器,直接使用工具调用你的API。因此,服务器端必须对接收到的所有请求(无论是否跨域)实施完整的身份验证、授权和输入验证。CORS策略不是访问控制!它只是一个浏览器级别的同源策略扩展。在服务器日志中监控异常的Origin头部,可以帮助你发现潜在的扫描或攻击行为。

生产环境配置最佳实践

1. 环境隔离:开发环境可以宽松(但也不建议用"*"),生产环境必须严格。使用配置文件或环境变量管理白名单。

2. 定期审计:定期审查你的CORS配置,确保白名单中的域名仍然有效且必要。

3. 结合其他安全头部:与Content Security Policy、Strict-Transport-Security等安全响应头协同使用,构建纵深防御。

4. API网关统一管理:在微服务架构中,在API网关层统一实施CORS策略,避免每个服务单独配置可能产生的不一致和遗漏。

# Nginx 配置示例
location /api/ {
    if ($http_origin ~* (https?://(www\.)?(yourdomain\.com|trusted\.com)$)) {
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
    }
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }
    # ... 其他代理规则
}

总结:将CORS视为重要的安全边界

CORS配置绝非一个简单的技术开关。它是一个定义“谁可以跨域访问我的资源”的严格安全策略。将其视为应用程序的重要安全边界之一。通过实施精确到源、方法和头部的白名单策略,充分利用预检请求机制,并牢记携带凭证时的限制,你可以显著降低跨域攻击导致的信息泄露风险。永远记住,安全是一个持续的过程,对CORS策略的定期审查和更新,应成为你安全运维的常规组成部分。