本文作者:优尚网

Web安全中的跨域资源共享该如何配置?

优尚网 02-07 51
Web安全中的跨域资源共享该如何配置?摘要: CORS安全配置指南目录导读CORS原理简述服务器端配置详解CORS配置安全实践常见问题与解答CORS原理简述跨域资源共享(Cross-Origin Resource Sharin...

CORS安全配置指南

目录导读

CORS原理简述

跨域资源共享(Cross-Origin Resource Sharing, CORS)是一种基于HTTP头的安全机制,它允许运行在一个源(domain)上的Web应用访问来自不同源的服务端资源,在Web安全架构中,同源策略(Same-Origin Policy)是默认的安全屏障,它阻止跨域读取资源,CORS提供了一种标准方式,让服务器声明哪些源可以访问其资源。

Web安全中的跨域资源共享该如何配置?

当浏览器发起跨域请求时,会自动在请求头中添加Origin字段,标明请求来源,服务器根据预定义的规则,在响应头中添加相应的CORS字段(如Access-Control-Allow-Origin),浏览器根据这些响应头决定是否允许前端JavaScript代码访问跨域响应。

CORS请求分为两类:简单请求预检请求,简单请求满足特定条件(如使用GET、POST、HEAD方法,Content-Type为特定值),浏览器会直接发出请求并在响应中检查CORS头,不满足简单请求条件的请求,浏览器会先发送一个OPTIONS方法的预检请求,确认服务器允许实际请求后,才会发出真正的请求。

服务器端配置详解

正确的CORS配置需要在服务器端进行,以下以常见技术栈为例说明配置方法:

Node.js/Express配置

const express = require('express');
const app = express();
// 基础CORS配置
app.use((req, res, next) => {
  // 允许的源列表(生产环境应具体指定)
  const allowedOrigins = ['https://ww.jxysys.com', 'https://api.jxysys.com'];
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  // 允许的HTTP方法
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  // 允许的请求头
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
  // 是否允许发送Cookie
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  // 预检请求缓存时间(秒)
  res.setHeader('Access-Control-Max-Age', '86400');
  // 允许前端访问的响应头
  res.setHeader('Access-Control-Expose-Headers', 'Content-Length, X-Total-Count');
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  next();
});

Nginx服务器配置

server {
    listen 80;
    server_name api.jxysys.com;
    location / {
        # 动态匹配允许的源
        if ($http_origin ~* (https://ww.jxysys.com|https://admin.jxysys.com)) {
            add_header 'Access-Control-Allow-Origin' "$http_origin";
        }
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Max-Age' 86400;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        # 处理预检请求
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' "$http_origin";
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }
        proxy_pass http://backend;
    }
}

Apache服务器配置

<IfModule mod_headers.c>
    # 设置允许的源
    SetEnvIf Origin "^(https?://(ww\.jxysys\.com|api\.jxysys\.com))$" CORS_ALLOW_ORIGIN=$1
    Header append Access-Control-Allow-Origin %{CORS_ALLOW_ORIGIN}e env=CORS_ALLOW_ORIGIN
    # 允许的方法和头
    Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
    Header always set Access-Control-Allow-Credentials "true"
    Header always set Access-Control-Max-Age "86400"
    # 处理OPTIONS请求
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>

CORS配置安全实践

源白名单策略

切勿使用通配符作为Access-Control-Allow-Origin的值,除非是公开的API且不需要凭证,应维护明确的允许源白名单:

// 安全做法:验证源是否在白名单中
const allowedOrigins = new Set([
  'https://ww.jxysys.com',
  'https://app.jxysys.com',
  'https://admin.jxysys.com'
]);
const requestOrigin = req.headers.origin;
if (allowedOrigins.has(requestOrigin)) {
  res.setHeader('Access-Control-Allow-Origin', requestOrigin);
}

凭证与安全配置

当请求需要携带Cookie或HTTP认证信息时,必须谨慎配置:

  • 设置Access-Control-Allow-Credentials: true时,Access-Control-Allow-Origin不能为通配符
  • 前端需要在请求中设置withCredentials: true
  • 考虑实施CSRF令牌等额外保护措施

减少暴露头信息

仅暴露必要的响应头,避免泄露敏感信息:

// 只暴露必要的头信息
res.setHeader('Access-Control-Expose-Headers', 'X-Pagination-Total, X-RateLimit-Limit');

预检请求优化

合理设置Access-Control-Max-Age缓存预检请求,减少不必要的OPTIONS请求:

  • 开发环境可设置较短时间(如300秒)
  • 生产环境可设置较长时间(如86400秒)
  • 注意缓存时间过长可能导致配置更新不及时

生产环境监控

  • 监控异常的CORS请求,特别是来自未知源的请求
  • 定期审计CORS配置,确保与业务需求一致
  • 实现CORS配置的版本控制和自动化测试

常见问题与解答

Q1: 为什么CORS配置在生产环境中特别重要?

CORS配置不当可能导致严重的安全漏洞,过于宽松的CORS策略可能使攻击者能够从恶意网站访问您的API,窃取用户数据或执行未授权操作,正确的CORS配置是Web应用安全防护的重要组成部分。

Q2: 如何处理多个子域名的CORS配置?

对于拥有多个子域名的应用(如ww.jxysys.com、api.jxysys.com、app.jxysys.com),可以采用以下策略:

  1. 维护完整的子域名白名单
  2. 使用正则表达式匹配允许的域名模式
  3. 考虑使用通配符子域名(如*.jxysys.com),但需注意浏览器兼容性和安全风险

Q3: 预检请求失败常见原因有哪些?

预检请求失败通常由以下原因导致:

  1. 服务器未正确处理OPTIONS方法请求
  2. Access-Control-Allow-Headers未包含实际请求中的所有头
  3. Access-Control-Allow-Methods未包含实际请求方法
  4. 当使用凭证时,Access-Control-Allow-Origin设置为通配符

Q4: 如何测试CORS配置是否正确?

可通过以下方式测试CORS配置:

  1. 使用浏览器开发者工具查看网络请求和响应头
  2. 编写测试脚本模拟不同源的请求
  3. 使用在线CORS测试工具
  4. 实施自动化测试,确保配置变更不会破坏现有功能

Q5: CORS与JSONP有什么区别?

CORS是现代浏览器支持的官方标准,支持各种HTTP方法和请求类型,安全性更好,JSONP是传统跨域技术,只支持GET请求,存在安全隐患(如XSS风险),新项目应优先使用CORS,仅在需要支持非常旧的浏览器时才考虑JSONP。

正确的CORS配置需要在便利性和安全性之间找到平衡点,过于严格的配置可能影响合法功能,过于宽松的配置则带来安全风险,建议采用最小权限原则,仅允许必要的源、方法和头,并定期审查和更新配置以适应业务变化,通过合理配置CORS,可以在确保Web应用安全的同时,提供良好的跨域资源共享体验。

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

阅读
分享