通过配置回源请求头实现简单的 CDN 回源鉴权

CDN(内容分发网络)的核心原理,是将网站内容缓存到分布在各地的边缘节点。当用户访问网站时,请求会被自动调度到距离最近、负载最合适的节点,由节点直接返回内容,而不必每次都访问源站服务器。

获取 EdgeOne 回源 IP 网段

为了让 CDN 的防护效果最大化,在使用 CDN 后隐藏回源域名并限制访问,是一项必要的操作(?)。一种比较有效的方法是:源站的回源域名仅允许 CDN 节点 IP 段访问,而其他访客只能通过 CDN 代理后的加速域名进行访问。这一点通过配置 Nginx 即可轻易实现。

腾讯云的EdgeOne CDN提供了CDN节点的IP段,需要在安全防护-源站防护中手动开启该功能。

需要注意的是,腾讯云 EdgeOne 会不定期更新回源 IP 网段,因此必须在 IP 段变更前及时更新服务器 Nginx 配置中允许回源的 IP 段,否则回源失败,CDN连不上源站就直接炸了。

参考边缘安全加速平台 EO 源站防护(获取/更新 EdgeOne 回源 IP 网段)文档,我们可以写一个 Python 程序来自动更新 IP 段(实则是由ChatGPT和Gemini帮我写的)

# pip install tencentcloud-sdk-python
# 可添加每日定时任务来自动执行该文件
# 直接在Nginx配置中用include引用生成的teo_real_ip.conf即可

import json
import os
from tencentcloud.common import credential
from tencentcloud.teo.v20220901 import teo_client, models

# --- 配置区 ---
SECRET_ID = "你的SecretId"
SECRET_KEY = "你的SecretKey"
ZONE_ID = "你的ZoneId"
# 生成的 Nginx 配置文件路径
CONF_FILE_PATH = "/www/server/nginx/conf/teo_real_ip.conf"
# --- --- ---

def fetch_teo_ips():
    try:
        cred = credential.Credential(SECRET_ID, SECRET_KEY)
        client = teo_client.TeoClient(cred, "")

        req = models.DescribeOriginACLRequest()
        req.ZoneId = ZONE_ID

        resp = client.DescribeOriginACL(req)
        data = json.loads(resp.to_json_string())

        # 获取当前 IP 和 即将生效的 IP (NextOriginACL)
        # 建议两者都加上,确保在 IP 切换期间平滑过渡
        acl_info = data.get("OriginACLInfo", {})
        ipv4_list = acl_info.get("CurrentOriginACL", {}).get("EntireAddresses", {}).get("IPv4", [])

        next_acl = acl_info.get("NextOriginACL")
        if next_acl:
            next_ipv4 = next_acl.get("EntireAddresses", {}).get("IPv4", [])
            ipv4_list = list(set(ipv4_list + next_ipv4))  # 合并去重

        return ipv4_list
    except Exception as e:
        print(f"获取 IP 失败: {str(e)}")
        return None


def update_nginx_config(ips):
    if not ips:
        return

    content = "# 腾讯云 EdgeOne 回源 IP 段自动更新\n"
    content += "real_ip_header X-Forwarded-For;\n"
    content += "real_ip_recursive on;\n\n"

    for ip in ips:
        content += f"set_real_ip_from {ip};\n"

    with open(CONF_FILE_PATH, "w") as f:
        f.write(content)

    # 重载 Nginx (宝塔环境命令)
    os.system("/etc/init.d/nginx reload")
    print("TEO IP 段已更新,Nginx 已重载")


if __name__ == "__main__":
    ips = fetch_teo_ips()
    if ips:
        update_nginx_config(ips)

代码理论上是没问题的,但是运行后报错提示区域没attribute。研究了一会才发现,腾讯云 EdgeOne 免费套餐并不提供源站防护功能,因此自然没有权限调用相关接口。

当然,GitHub 上其实有不少更新腾讯云 CDN IP 网段的仓库,考虑到 IP 段变更周期通常较长(数月一次),手动复制也不是不行。只是,仅仅是公开几个全网可查的回源 IP 段,腾讯云免费版套餐却无法调用,我只想说:

这很腾讯。

自动更新白名单 IP 段的方案在免费版套餐中暂时走不通(我们白嫖党是这样子的),上面的尝试先保留在此。

header鉴权

当 CDN 边缘节点未命中缓存,或请求的内容本身不允许缓存(如动态接口)时,边缘节点会向源站服务器回源获取数据,并将结果返回给用户。

因此,访客访问加速域名时,所有请求都会先到达 CDN 边缘节点;只有在缓存未命中的情况下,请求才会由边缘节点转发至源站服务器。

我们可以在 CDN 向源站回源的请求中加入特定的请求头字段,并在源站的 Web 服务器中校验该请求头作为“暗号”。

当请求携带正确的请求头时,说明该请求是由 CDN 节点回源发起的,可以正常放行;反之,则说明存在访客绕过 CDN 直接访问源站的行为,源站可直接返回 444 拒绝连接。

由于用户的所有访问请求都会先到达 CDN,再由 CDN 统一回源,因此通过 CDN 正常访问的请求,在回源阶段必然会携带该请求头,从而实现对源站的有效保护。

这里假设我们在 Header 中加入这样一个字段,用于 CDN 回源节点与源站 Nginx 之间的鉴权:

X-Origin-Token: Your_Secret_Code

整个流程大概是这样子的:

无对应的header字段:

浏览器
  ↓(没有 X-Origin-Auth)
源站 Nginx
  ↓
if ($http_x_origin_auth != "密钥") → return 444 → 关闭连接

有对应的header字段:

浏览器
  ↓
CDN 边缘节点
  ↓(EdgeOne规则引擎:CDN 在“回源请求”里自动加 X-Origin-Auth)
源站 Nginx
  ↓
校验通过 → 正常返回

配置

我们需要在 CDN 节点的回源请求中加入这个字段(字段名和内容可自定义,建议尽量复杂):

然后修改服务器的Nginx配置:

# --- EO CDN回源 header 鉴权 ---
# 检查回源请求头是否匹配
if ($http_x_origin_token != "Your_Secret_Code") {
    # 如果不匹配,说明是绕过 CDN 的直连,直接断开连接不给响应
    return 444; 
}

# 获取真实 IP ,理论上请求都是来自 EdgeOne 的流量,保证了X-Forwarded-For真实性;
# 为了防止服务器的WAF因某些原因把CDN的回源ip给拦了,这里信任了0.0.0.0/0(生产环境慎用)
# real_ip_header X-Forwarded-For;
# set_real_ip_from 0.0.0.0/0; 
# real_ip_recursive on;

理论上就ok了。

可以看到,直接用源站域名连接是超时的,而通过CDN访问则正常。


小结

要想彻底保护源站,最保险的方法还是限制 CDN IP 段,这是直接从底层逻辑进行防御了。黑客即便猜到了设置的 Header 暗号,如果他没 CDN 节点转发请求,也连不上的服务器。而使用回源鉴权对暗号呢,一旦有人截获了 Header 名字和内容,就可以直接伪造这个 Header 进行攻击

当然,本文所描述的 Header 鉴权只是一种最简单、直接明文传输的初级方法,虽然有一点用,但是Header及密钥一旦泄露/被截获,就完犊子了。

有关回源鉴权的更多形式,还请参见这篇文章

系列文章
学习笔记/技术
Contents: 26/26
License
许可协议:CC BY-SA 4.0
原文链接:https://loneapex.cn/archives/4932
暂无评论

发送评论 编辑评论

这里支持部分markdown语法哦!
评论发布后您随时可以修改。

				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯
 ̄﹃ ̄
(/ω\)
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯
φ( ̄∇ ̄o)
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
(´っω・`。)
( ,,´・ω・)ノ)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•)
(ㆆᴗㆆ)
from『不动声色的柏田与喜形于色的太田』
from『邻座的邻座的艾莉同学』 | 最喜欢有希酱啦!(素材来自bilibili@最上川下山)
from『魔女的夜宴』 | Mimosa整理
Source: github.com/k4yt3x/flowerhd
整活by Mimosa233
颜文字
柏田
周防有希
魔女
小恐龙
花!
夸夸我!
上一篇