网站运营中把CDN域名和源站域名分离,很多人只考虑到加速和隐藏源站IP,却忽略了一个致命的安全隐患——Cookie泄露。当你把CDN直接指向源站域名,或者让CDN域名与源站共用同一个顶级域时,用户登录后的会话Cookie就会通过CDN节点被大量暴露。这个问题在电商、金融、SaaS平台这类强依赖登录态的网站上尤其严重,一旦Cookie被中间人截获或者通过CDN日志泄露,攻击者可以直接伪造用户身份,无需密码就能接管账户。

问题的根源在于浏览器的同源策略和Cookie的Domain作用域机制。假设你的源站域名是www.example.com,用户登录后服务器会下发一个Set-Cookie响应头,默认情况下这个Cookie的Domain属性就是www.example.com。如果你把CDN直接CNAME到www.example.com,或者让CDN回源时携带了Host头为www.example.com,那么CDN边缘节点在缓存页面时,很可能会把包含Set-Cookie的响应头一并缓存下来。更严重的是,如果你把静态资源也放在www.example.com这个域名下,每一次图片、CSS、JS的请求都会自动带上Cookie,这些请求经过CDN节点时,Cookie信息就会被记录在CDN的访问日志中。国内主流CDN厂商的日志系统通常保留7到180天不等,这些日志一旦因为权限配置不当或者内部人员泄露而流出,就等于把大量用户的会话凭证拱手送人。

Cookie的Domain作用域决定了安全边界

Cookie的Domain属性设定了这个Cookie在哪些子域下会被浏览器自动发送。如果你把Domain设置为example.com,那么所有以.example.com结尾的子域,包括www.example.com、api.example.com、static.example.com,都会在请求时携带这个Cookie。很多开发者在配置Cookie时图省事,直接把Domain写成顶级域,这样单点登录做起来方便,但同时也把安全风险扩散到了整个域名体系。一旦你的CDN域名也被纳入这个作用域范围,比如你把静态资源部署在cdn.example.com,而Cookie的Domain又是example.com,那么用户访问cdn.example.com上的任意资源时,浏览器都会把登录Cookie发送过去。CDN节点接收到这些Cookie后,虽然通常不会主动利用,但日志记录、错误监控、甚至是第三方插件都可能把这些敏感信息泄露出去。

CDN缓存策略不当导致的Cookie缓存风险

CDN的缓存机制通常以URL和部分请求头作为缓存键。如果源站服务器在返回静态资源时,不小心同时输出了Set-Cookie响应头,CDN节点会把这个Set-Cookie当作普通响应头一起缓存下来。接下来其他用户请求同一个URL时,CDN会直接把缓存的响应返回,其中就包含了前一个用户的Set-Cookie值。这会导致两个严重后果:一是用户A拿到的Cookie实际上是用户B的,造成身份错乱;二是攻击者可以主动构造请求,让CDN缓存一个带有恶意Cookie的响应,然后诱导受害者访问这个URL,从而实现Cookie固定攻击。这种情况在配置失误的Nginx反向代理环境中非常常见,源站把动态页面错误地标记为可缓存,或者CDN的回源规则没有正确区分静态和动态内容。

域名分离的核心原则:静态资源使用独立的无Cookie域

解决这个问题的标准做法是把静态资源部署在一个完全独立的域名上,这个域名与主站域名不能有相同的顶级域后缀,或者说至少不能共享Cookie的Domain作用域。比如主站是www.example.com,静态资源域名应该使用examplecdn.com或者static-example.com这种完全不同的域名,确保浏览器不会把主站的Cookie发送到静态资源域。如果你必须使用主站的子域,比如static.example.com,那就要严格保证所有Cookie的Domain属性只设置为www.example.com,而不是example.com。这样static.example.com就不会收到Cookie。但这样做会牺牲单点登录的便利性,所以需要在安全性和用户体验之间做权衡。对于大多数商业网站来说,使用完全不同的域名来做CDN加速是更安全的选择。

源站与CDN之间的回源请求头处理

CDN回源时,通常会携带原始客户端的部分请求头,其中就包括Cookie。如果你的源站不需要依赖Cookie来做缓存区分或者个性化处理,最安全的做法是在CDN配置中直接删除回源请求中的Cookie头。国内主流CDN厂商都支持回源请求头修改功能,你可以设置规则在回源时剥离Cookie。这样即使CDN边缘节点收到了用户的Cookie,也不会传递到源站,减少了源站侧泄露的风险。但如果你的源站需要根据Cookie做地域识别、AB测试或者用户画像,那就不能一刀切地删除Cookie,而是#这种情况下应该使用白名单机制,只允许特定的Cookie字段回源,其他一律过滤。

HTTPOnly、Secure、SameSite属性的正确配置

这三个Cookie属性是防御Cookie泄露的第一道防线,但很多运营人员配置的时候并不理解其真正作用。HTTPOnly标记会禁止JavaScript通过document.cookie读取Cookie,这能有效防御XSS攻击窃取Cookie,但对CDN日志泄露无能为力,因为日志记录的是HTTP请求头,不是JavaScript读取。Secure标记要求Cookie只能在HTTPS连接中传输,这能防止中间人攻击,但如果CDN节点本身已经解密了HTTPS流量,Secure标记就形同虚设。SameSite属性才是真正能限制跨站请求携带Cookie的利器,设置为Strict时,浏览器只会在同站请求中发送Cookie,跨站的图片、链接都不会携带;设置为Lax时,顶级导航的GET请求会携带,但CSRF攻击常用的POST请求不会携带。对于登录会话Cookie,建议设置为SameSite=Lax并配合Secure和HTTPOnly,这样能在兼容性和安全性之间取得平衡。

CDN日志的安全管理容易被忽视

CDN的访问日志通常会记录完整的请求URL、Referer、User-Agent以及请求头中的Cookie信息。这些日志如果存储在不加密的OSS上,或者被没有权限管控的内部人员随意下载,就会成为数据泄露的重灾区。很多运营团队只关注CDN的加速效果和带宽成本,日志安全根本不在考核指标里。正确的做法是:第一,在CDN控制台开启日志ble日志脱敏功能,把Cookie、Authorization等敏感头做掩码处理;第二,设置日志的访问权限和生命周期,只允许安全审计人员访问,并且30天后自动删除;第三,如果业务上确实需要分析日志,应该把日志导入到内部的大数据平台,在导入环节就做脱敏处理,原始日志不落盘。

实际部署中的域名分离架构示例

假设你的主站域名为www.example.com,源站服务器IP为192.168.1.100。安全的CDN部署架构应该是这样的:创建一个CNAME记录,把cdn.example.com指向CDN厂商提供的加速域名,但这个cdn.example.com只用于托管静态资源,不回源到主站服务器,而是回源到一个专门的对象存储Bucket。主站www.example.com也接入CDN,但配置为仅加速动态内容,缓存时间设为0,同时开启回源请求头过滤,删除Cookie头。用户访问www.example.com时,HTML页面通过动态加速返回,页面中引用的CSS、JS、图片全部使用cdn.example.com这个域名。由于两个域名完全不同,浏览器不会把www.example.com下的Cookie发送到cdn.example.com。如果业务需要用户上传头像这类动态生成的图片,可以在源站服务器上设置一个内部接口,生成图片后上传到对象存储,返回cdn.example.com的URL,而不是直接通过源站域名提供图片访问。

源站Nginx配置中的安全加固

在源站Nginx的配置中,有几项设置能进一步减少Cookie泄露风险。首先,对于静态文件类型的请求,直接返回404或者重定向到CDN域名,不要让源站处理这些请求。配置如下:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    return 301 https://cdn.example.com$request_uri;
}

其次,在返回响应头时,明确设置Cache-Control,确保动态内容不被中间节点缓存:

location / {
    add_header Cache-Control "private, no-cache, no-store, must-revalidate";
    add_header Pragma "no-cache";
    add_header Expires "0";
}

最后,如果你的Cookie只需要在HTTPS下传输,务必加上Secure标记,在Nginx中可以通过设置Cookie时追加参数来实现,或者在应用层统一配置。如果是使用Tomcat或者Node.js等应用服务器,同样需要在设置Cookie时指定这些安全属性。

多子域场景下的Cookie隔离策略

对于大型网站,经常会有多个子域,比如mail.example.com、pay.example.com、blog.example.com。如果所有子域共享同一个SSO登录态,Cookie的Domain通常会设置为example.com。这种情况下,CDN域名的选择就更加重要。绝对不能使用任何以example.com结尾的子域来做CDN加速,否则所有子域的Cookie都会被发送到CDN。正确的做法是使用一个完全不相关的域名,比如example-static.com,或者使用CDN厂商提供的默认域名。如果出于品牌考虑必须使用自己的域名,那就注册一个全新的域名专门用于静态资源,并在DNS层面做好隔离。同时,在应用层实现SSO时,不要依赖Cookie的跨子域共享,而是使用独立的认证中心域名,通过重定向和Token传递来实现跨子域登录,这样每个子域的Cookie作用域都可以限制在本域内。

监控与审计是最后一道防线

即使做了以上所有配置,仍然需要建立监控机制来及时发现异常。在CDN层面,开启异常访问告警,当某个IP在短时间内请求了大量带有不同Cookie的URL时,可能是在进行Cookie爆破或者爬取。在源站层面,记录所有携带Cookie的静态资源请求,正常的用户行为不会对静态资源发送Cookie,如果日志中出现大量这类请求,说明配置可能有漏洞。定期审计CDN的回源配置和缓存规则,确保没有意外缓存了包含Set-Cookie的响应。安全是一个持续的过程,域名分离只是其中一环,但这一环如果做不好,其他环节的安全投入都可能白费。