
先说一个我踩过的坑为了隐藏源站IP我把域名套到了CDN后面源站在防火墙里关了所有端口只留了443。结果没过多久源站IP还是被人扫到了。查来查去问题就出在SSL证书上——CDN回源需要拿到SSL证书而这个证书在公网上留下了太多痕迹。而且更尴尬的是当时源站和CDN用的是同一张证书扫描器只要全网抓一遍443端口把证书指纹一比对源站IP就直接暴露了。后来我把“源站证书”和“CDN证书”彻底拆开再做了一组访问限制才把这个坑填上。这篇文章不聊虚的我会从证书泄露源站IP的原理讲起把最常见的泄露途径拆出来然后给出可以直接照做的隐藏源站IP方案。无论你的源站是单台服务器还是内网负载均衡只要用了CDN这几条经验都能用上。1. 先搞清楚SSL证书是从哪一步把源站IP“卖”出去的很多人以为只要把域名解析指向CDN源站IP就藏住了。但SSL证书这条链路上其实有多个环节会把源站真实IP暴露出去。下面这三个环节是最典型的。1.1 证书透明度日志所有公开证书都会留底证书透明度Certificate Transparency简称CT是公开的证书签发审计系统。CA在签发每一张公网SSL证书时几乎都会把证书信息写入多个公开的CT日志服务器。这个机制的初衷是防止CA乱发证书、防止证书被恶意滥用但它也带来了一个副作用证书的全量公开记录可以被任何人查询和下载。这就意味着只要你的源站使用了一张公网CA签发的证书这张证书的域名、签发时间、公钥指纹、证书序列号等信息就一定会出现在公开日志里。攻击者不需要攻击你的服务器只需要盯住CT日志里的新增证书再结合全网IP扫描就能把证书和IP关联起来。那么CT日志和源站IP是怎么关联上的核心玩法是扫描器比如使用全网扫描引擎跑一遍公网IPv4的443端口把每个IP返回的证书指纹抓下来再去CT日志里找同样的指纹。只要你的源站也对外返回了同一张证书这个IP地址就等于被“标记”了。哪怕你的CDN节点也返回同一张证书由于CDN节点数量巨大扫描器需要进一步辅助判断才会命中源站——但源站一旦也暴露在公网首先会被普通扫描器识别。提示CT日志本身是合规且重要的安全机制这里不是建议逃避它而是要意识到公开证书会带来额外的暴露面。源站内部使用的证书完全可以不申请公网证书。1.2 源站与CDN共用同一张证书的连锁反应这是新手最容易犯的错误我为了让CDN回源不报证书错误直接把CDN上使用的证书复制了一份配置到源站Nginx上。结果就是源站返回的证书、CDN节点返回的证书甚至配置文件里的证书路径都完全一致。这种做法最大的问题是证书指纹一致导致源站IP和CDN节点在扫描结果里“长得一模一样”。而CDN节点数量多源站IP只有一个攻击者只需要对返回相同证书的IP逐个做后续探测比如看HTTP响应头、TLS扩展、响应延迟、访问特定路径的表现就能把源站IP从一堆CDN节点中筛出来。更麻烦的是如果源站Web服务器配置不当还会直接返回“过于完整”的响应。比如源站Nginx配置了server_name _;任何IP访问443端口都能看到证书和网站内容那这台服务器几乎就是裸奔状态和CDN无关的流量也能访问。这个场景我见得太多了尤其是在一些“伪静态防火墙”配置不完整的服务器上。1.3 扫描器怎么通过证书匹配源站IP先理解扫描器的大致思路才能对症下药。全网扫描通常分三步对公网IP的常用端口比如443、8443做TCP连接拿到TLS握手数据。从TLS握手数据中提取服务器证书计算证书指纹SHA-1或SHA-256摘要也可以提取证书里的CN、SAN、序列号。把提取到的指纹和CT日志、证书查询平台的数据做交叉匹配。只要源站IP直接向公网开放了443端口并且返回了和CDN相同的证书扫描器就能在几小时内把你找出来。即使你换了源站端口比如改用非标准端口8443、7443扫描器也会扫全端口范围TLS指纹一样能匹配上。明白了这个原理就能反推出防御思路只要源站IP在TLS层返回的证书和CDN对外返回的证书不是同一张扫描器就无法直接通过证书指纹关联到源站。同时再配合访问来源限制让非CDN回源IP根本拿不到TLS握手数据这才是真正有效的隐藏方案。2. 核心解决思路把源站证书和CDN证书彻底分开解决证书泄露源站IP问题不是靠“删掉证书日志记录”也不是靠“把敏感字段隐藏起来”而是要从架构上割裂两者的关联。概括起来就一句话CDN对外用的是一张公网证书源站回源用的是另一张“私有证书”或自签证书两者完全独立。2.1 为什么分开比隐藏更有效隐藏的问题在于你永远要面对“信息泄露点”。只要有证书出现在源站IP的TLS握手里CT日志和扫描器就能产生关联。与其想着怎么隐藏不如直接让源站不存在“公网证书”这种高辨识度信息。分开之后的效果是扫描器访问源站IP拿到的是自签证书或内部CA证书指纹完全不同。CT日志里根本搜不到这张私有证书因为私有证书不会提交到公开CT日志。CDN节点与源站之间仍然能正常工作因为CDN回源可以开启“跳过证书校验”或者校验我们自定义的私有CA。所以在CDN架构里最合理的方案是CDN对外继续使用CA签发的公网证书源站则使用自签证书或内部CA证书并且仅允许CDN回源IP访问。2.2 源站证书的两种选择自签证书和内部CA证书源站证书可以选用自签证书也可以搭建一个内部的私有CA给源站签发证书。两者的区别在于管理成本和信任配置。自签证书最轻量一条命令就能生成Nginx上配置后CDN回源时关闭证书校验就能正常使用。缺点是如果源站不止一台每台都要单独生成且证书名称、有效期管理比较乱。内部CA证书更适合多服务器场景。你可以在一台内网机器上搭建私有CA根证书然后用它给多台源站签发证书。CDN回源时把私有CA根证书上传到CDN的“回源证书校验”配置里这样CDN就能验证源站证书是否可信安全性和可管理性都更好。以我常用的自签方案为例生成一张有效期5年的源站证书用OpenSSL就能完成# 生成私钥和自签证书 openssl req -newkey rsa:2048 -nodes -keyout /etc/nginx/ssl/origin.key \ -x509 -days 1825 -out /etc/nginx/ssl/origin.crt \ -subj /CCN/STBeijing/LBeijing/OInternal/CNorigin.local # 查看证书指纹确认和CDN证书不同 openssl x509 -in /etc/nginx/ssl/origin.crt -noout -fingerprint注意这里CN填的是origin.local不要填真实域名。因为源站证书本来就是给CDN回源校验用的不需要对外。如果填了真实域名CT日志虽然没有记录但万一源站IP被扫描器访问返回的证书CN会直接暴露你的域名这又是一个不必要的线索。2.3 回源方式的取舍HTTP回源、HTTPS回源和回源HOST解决了证书问题后还需要确定回源方式。CDN回源主要有两种回源HTTPCDN节点拉着真实域名访问源站80端口。这种方式最简单源站甚至不用配置443端口。它天然避免证书泄露因为源站根本不对外提供TLS握手。回源HTTPSCDN节点以HTTPS协议回源到源站443端口。此时需要配置源站证书通常就是上面说的自签证书或内部CA证书。如果你对源站有合规要求或者源站本身只能对外提供HTTPS那就只能走HTTPS回源。否则我更推荐回源HTTP。很多CDN服务商的回源模式都默认支持HTTP回源你把源站Nginx的80端口只开放给CDN回源IP对外关闭其他访问就不存在SSL证书泄露问题。还有一个容易被忽视的参数是“回源HOST”。CDN回源时会携带一个HOST头这个HOST应该和源站上配置的站点域名保持一致。处理好回源HOST才能保证CDN回源请求被正确路由到站点配置同时避免因为证书CN不匹配引发回源失败。注意如果你的源站已经跑着其他业务或者必须要用HTTPS回源那么自签证书方案就足够。不要因为图省事让源站继续沿用CDN公网证书这是泄露源站IP的最快捷径。3. 实操一套完整的源站IP隐藏配置流程下面以一台Nginx源站、一台常见CDN为例把从确认回源IP到验证隐藏效果的完整流程走一遍。这套流程在主流Linux发行版上都能用命令可以直接复制改参数。3.1 第一步确认CDN回源IP段防火墙规则必须建立在准确的回源IP基础上。如果规则写错要么该放行的回源IP没放行导致网站无法访问要么把其他IP放进来导致隐藏失效。获取CDN回源IP段的方法通常有三个在CDN服务商后台找到“回源IP段”或“节点IP段”的公开文档或者工单咨询。CDN回源日志中记录的源IP段但前提是你已经放行了部分IP。自己搭建一个测试源站观察CDN回源时使用的IP列表。强烈建议优先使用服务商公开的回源IP段并定期更新。因为CDN回源IP段是会调整的一旦IP段更新你的防火墙规则也可能需要同步更新。拿到回源IP段后先整理成一份清单。比如回源IP段可能是203.0.113.0/24、198.51.100.0/24这样的后面写防火墙规则时直接使用。3.2 第二步为源站生成专用证书并配置确认源站当前证书一定不是CDN对外的那张公网证书。然后用上面的OpenSSL命令生成一张新的自签证书。生成好后修改Nginx的SSL配置。关键配置示例server { listen 443 ssl; server_name origin.example.com; # 或者一个内网标识域名 ssl_certificate /etc/nginx/ssl/origin.crt; ssl_certificate_key /etc/nginx/ssl/origin.key; ssl_protocols TLSv1.2 TLSv1.3; # 默认不处理未匹配的请求避免被扫描器直接命中 server_name_in_redirect off; client_max_body_size 20m; root /var/www/html; index index.php index.html; }注意这里的server_name可以设置为源站内部使用的域名但没太大意义因为CDN回源时通常会通过HOST头覆盖域名。建议直接在server块中写一个占位域名同时配合下面的来源限制策略让外部扫描器无法正常访问。3.3 第三步防火墙限制回源IP访问这是隐藏源站IP最关键的一步。不管证书配置成什么样源站IP都应该只响应CDN回源IP的请求。一般通过防火墙在OS层就完成过滤。以firewalld为例创建两个zone放行回源IP段到443/80端口# 创建自定义firewalld服务可选也可以直接放行端口 firewall-cmd --permanent --new-servicecdnsrc firewall-cmd --permanent --servicecdnsrc --set-descriptionCDN Source Access firewall-cmd --permanent --servicecdnsrc --add-port80/tcp firewall-cmd --permanent --servicecdnsrc --add-port443/tcp # 将回源IP段加入白名单 zone firewall-cmd --permanent --zonetrusted --add-source203.0.113.0/24 firewall-cmd --permanent --zonetrusted --add-source198.51.100.0/24 firewall-cmd --permanent --zonetrusted --add-servicecdnsrc # 重载防火墙规则 firewall-cmd --reload如果使用iptables思路也类似先放行回源IP的80/443端口最后默认DROP所有外部访问。这里有一个很关键的教训放行规则一定要写在DROP规则之前。如果你先执行了iptables -P INPUT DROP再执行放行规则可能已经晚了。建议按顺序来iptables -A INPUT -s 203.0.113.0/24 -p tcp --dport 443 -j ACCEPT iptables -A INPUT -s 203.0.113.0/24 -p tcp --dport 80 -j ACCEPT iptables -A INPUT -s 198.51.100.0/24 -p tcp --dport 443 -j ACCEPT iptables -A INPUT -s 198.51.100.0/24 -p tcp --dport 80 -j ACCEPT # 其他流量默认拒绝 iptables -A INPUT -p tcp --dport 443 -j DROP iptables -A INPUT -p tcp --dport 80 -j DROP注意不同CDN服务商的回源IP段数量可能很多有的还区分IPv6。尽量同时把IPv6回源IP段也加进去否则IPv6流量可能会绕过白名单。如果你源站不启用IPv6公网访问最好直接关闭源站的IPv6地址分配避免暴露。3.4 第四步调整源站Web服务器响应即使有了防火墙白名单Web服务器层也应该做二次防护防止防火墙被误关闭或手动修改后出现空窗期。主要有两个操作第一设置默认站点不返回任何内容。在Nginx中增加一个默认server块catch-all访问请求直接返回444状态码Nginx特有的不返回响应并关闭连接server { listen 80 default_server; listen 443 ssl default_server; ssl_certificate /etc/nginx/ssl/default.crt; ssl_certificate_key /etc/nginx/ssl/default.key; return 444; }注意这个default server也需要一张证书可以用自签证书否则外部访问时TLS握手还是会暴露某个证书。由于default server的证书和业务证书不同扫描器就无法通过证书指纹匹配到原CDN证书。第二对于正常业务站点增加HOST校验只处理指定域名。下面的配置保证只有CDN回源时携带的HOST头是正确域名才会真正响应否则一律拒绝server { listen 80; listen 443 ssl; server_name www.example.com; # 仅允许CDN回源HOST if ($host ! www.example.com) { return 444; } # 其他业务配置... }Nginx的$host变量在回源请求中就是请求的HOST头必须和CDN配置的回源HOST一致。CDN配置回源HOST时填你对外服务的域名。这样即使有人知道了源站IP直接访问也会因为HOST不匹配而被拒。3.5 第五步验证与安全检查配置完成后不要急着收工。验证包括两层一是功能正常二是IP隐藏有效。功能验证模拟CDN回源请求curl -k -H Host: www.example.com --resolve www.example.com:443:源站IP https://www.example.com/这里的-k是因为源站证书是自签的如果不加-kcurl会报证书错误。正常情况应该返回网站内容。如果报错检查Nginx配置和回源HOST。隐藏验证直接访问源站IP但不带正确的HOST头curl -k https://源站IP/正常情况下这个请求会返回444或者连接被重置不会有任何证书内容返回。如果用openssl s_client去抓证书echo | openssl s_client -connect 源站IP:443 -servername www.example.com 2/dev/null | openssl x509 -noout -issuer -subject看到证书的CN不是你的真实域名而是类似origin.local那就说明源站对外返回的证书已经和CDN证书脱钩了。还可以用全端口扫描工具扫描源站IP确认非标准端口没有对外开放避免隐藏的漏洞。注意这里指的是自检不是攻击他人。4. 常见问题与排查技巧实录实际操作中遇到的问题很多有些问题看起来是证书配置错了其实另有原因。我把常见情况的排查思路整理了一些方便你按图索骥。4.1 为什么改完证书扫描器还是能发现源站这种情况往往是“源站还在用旧证书”。很多人改完Nginx配置后没有重启或者改了配置文件但实际加载的是缓存配置。优先排查nginx -t nginx -s reload还有一种可能你只改了Nginx证书但源站还开着其他服务比如FTP、SSH、邮件服务的SMTP端口这些服务可能携带主机名或证书信息。扫描器不一定只扫443还会扫大量常见端口。所以做完443配置后务必用端口扫描自检所有开放端口尽量只保留必要端口。另外很多云主机的“安全组”和“防火墙”是两层体系。如果你在云控制台安全组里放行了所有公网IP那么即使服务器内部iptables做了限制某些云平台的安全组规则仍然可能优先放行导致源站IP暴露。需要同时检查安全组和系统防火墙。4.2 如果源站IP已经泄露如何应急补救如果确认源站IP已经被公开扫描到、被写入威胁情报库甚至被人直接访问这时候要冷静处理。应急思路是先隔离再迁移。第一步立即在防火墙中放行CDN回源IP拒绝其他IP访问。第二步备份数据和配置清理掉源站中可能残留的恶意文件后申请一个新的源站IP。第三步新源站IP配置好所有规则之后再把CDN回源地址切到新IP。这里有个很实际的细节域名DNS记录里如果有历史解析记录指向源站IP攻击者可能会通过DNS历史查询拿到IP。所以应急时还需要关注旧DNS记录的删除以及确保新IP没有出现在任何历史记录中。不过DNS历史属于外部数据能做的只是尽量缩短旧记录存活时间。注意迁移源站IP是个大动作涉及证书、数据库连接、API回调等配置。建议提前做一套切换脚本记录所有需要改的配置项避免迁移时手忙脚乱把证书配置写错。4.3 证书过期和续期带来的隐藏坑源站自签证书过期后CDN回源会失败这是很多人遇到过的。因为自签证书没有自动续期机制只有手动重新生成并上传证书文件。一旦过期CDN回源会触发证书错误表现为用户端偶尔出现502/521错误因为CDN节点无法和源站建立TLS连接。解决方法是在源站证书到期前重新生成新证书并同步更新Nginx配置。不过要注意换证书时CDN回源校验是继续沿用旧的校验规则还是需要更新CDN配置不同服务商不一样。为了避免过期我通常会把自签证书有效期设成5年甚至10年同时在日历上设置一个提醒。对于需要频繁更换场景的可以写一个简单的定时任务检测证书剩余天数并告警。4.4 从日志和DNS历史中减少暴露源站IP暴露不只是证书一条路。Nginx默认日志会把真实IP记录在访问日志里如果日志传到第三方分析系统也可能泄露。建议在CDN节点回源时把源站日志中的客户端IP替换成CDN节点IP或者统一记录为回源IP这样即使日志泄露也不会直接暴露真实访客来源。DNS历史也是一个重要暴露面。很多域名在接入CDN之前直接解析到了源站IP。即使后来解析做了变更历史DNS记录仍然被第三方保存。你可以通过DNS历史查询工具检查是否有旧记录直接指向源站IP。如果有建议先修改DNS记录、再额外加一层CDN等待旧记录自然过期。不过DNS历史记录的时间依赖各家更新频率这个无法完全控制但至少可以避免未来继续产生新的源站IP记录。5. 几个我建议养成的防御习惯除了上面这些具体操作还有几个长期有效的防御习惯我总结出来供你参考。第一个习惯为源站单独做一个“隐藏专用域名”或“内部标识域名”。这个域名不发布到公网不在DNS正常解析记录中暴露只在CDN回源HOST和源站Nginx配置中使用。配合自签证书源站的辨识度会大大降低。第二个习惯对源站出站流量做限制。源站不要随意主动对外发起连接尤其是不要向未知IP发送HTTP请求避免被被动探测。可以通过防火墙限制源站只能访问CDN服务商的回源端口其他出站外联一律禁止。第三个习惯定期自查源站暴露面。可以使用在线扫描服务或自己写扫描脚本定期扫描源站IP的全端口和证书信息。一旦发现异常证书或新开放的端口立刻处理。安全不是一劳永逸尤其是CDN回源IP段更新、云平台安全组规则调整后旧规则很容易失效。我在实际处理这个问题的过程中最大的体会是隐藏源站IP从来不是某一个单一配置能搞定的而是一套组合拳。证书分离能解决CT日志和证书指纹匹配问题防火墙白名单能解决扫描器直连问题HOST校验能解决“拿已知IP直接访问”的问题。三者缺一效果就会大打折扣。尤其是证书分离这个动作如果你还在让源站和CDN共用一张公网证书那我建议你尽快拆开。改起来不复杂却能省掉后续一大堆麻烦。