如何在 CDN 边缘节点配置 Lua 脚本实现鉴权?

文章导读
直接在 CDN 边缘节点用 Lua 脚本做鉴权,适合自建 OpenResty 架构或支持边缘脚本(如阿里云 EdgeScript、腾讯云 ESA)的公有云环境,能在请求到达源站前拦截非法流量。
📋 目录
  1. 架构原理与优势
  2. 自建 OpenResty 完整配置
  3. 公有云边缘脚本适配
  4. 验证与日志排查
  5. 常见风险与规避
A A

直接在 CDN 边缘节点用 Lua 脚本做鉴权,适合自建 OpenResty 架构或支持边缘脚本(如阿里云 EdgeScript、腾讯云 ESA)的公有云环境,能在请求到达源站前拦截非法流量。

先说结论:边缘鉴权能显著降低源站压力,但不同 CDN 厂商的脚本环境差异很大,必须先确认平台支持能力再动手。

  • 适合:需要高频校验 Token、IP 黑白名单或防盗链的场景,且 CDN 服务商支持自定义脚本。
  • 先准备:确认 CDN 控制台是否开放 Lua 或类 Lua 脚本编辑权限,准备好鉴权算法逻辑(如 HMAC-SHA1)。
  • 安全警告:切勿将密钥硬编码在脚本中提交至版本库,建议使用环境变量或配置中心管理密钥。
  • 验收:上线前务必在灰度节点测试,确保正常请求不被误拦,异常请求能准确返回 403。

架构原理与优势

传统鉴权往往在源站应用层完成,这意味着所有请求(包括非法的)都会穿透 CDN 打到源站,消耗带宽和 CPU。将鉴权逻辑前置到 CDN 边缘节点,利用 Lua 脚本的高性能和非阻塞特性,可以在网络边缘直接丢弃非法请求。这样做的核心目的是“止血”,防止恶意流量浪费源站资源,同时减少用户等待响应的时间。

自建 OpenResty 完整配置

如果是自建 OpenResty 环境,需确保 Nginx 编译了 lua-nginx-module 模块。以下是一个包含完整 HMAC-SHA1 校验逻辑的可运行示例,实际使用时需替换密钥并建议通过环境变量加载。

1. http 块配置依赖:

http {
    lua_package_path "/path/to/your/lib/?.lua;;";
    
    # 建议将密钥存储在环境变量或外部配置,此处仅为示例
    lua_shared_dict auth_keys 10m;
    
    server {
        listen 80;
        server_name example.com;
        
        location / {
            access_by_lua_block {
                local hmac = ngx.hmac_sha1
                local encode_base64 = ngx.encode_base64
                
                -- 生产环境建议从 ngx.var 或 redis 获取密钥,避免硬编码
                local secret = "YOUR_SECRET_KEY"
                
                local args = ngx.req.get_uri_args()
                local token = args["token"]
                local expire = args["expire"]
                local uri = ngx.var.uri
                
                -- 1. 基础参数校验
                if not token or not expire then
                    ngx.status = 403
                    ngx.say("Missing auth parameters")
                    return ngx.exit(403)
                end
                
                -- 2. 过期时间校验 (允许前后 5 分钟误差需自行调整逻辑)
                if ngx.time() > tonumber(expire) then
                    ngx.status = 403
                    ngx.say("Token expired")
                    return ngx.exit(403)
                end
                
                -- 3. 签名计算与比对
                -- 签名源通常包含:过期时间 + 路径 + 随机数
                local sign_src = expire .. ":" .. uri
                local expected_sign = encode_base64(hmac(secret, sign_src))
                
                if token ~= expected_sign then
                    ngx.status = 403
                    ngx.say("Invalid signature")
                    return ngx.exit(403)
                end
            }
            
            proxy_pass http://origin_server;
        }
    }
}

2. 密钥安全建议:上述示例中的 secret 仅为演示。生产环境中,建议通过 ngx.var 读取环境变量,或使用 lua-resty-core 结合配置中心动态获取,避免密钥随代码泄露。

如何在 CDN 边缘节点配置 Lua 脚本实现鉴权?

公有云边缘脚本适配

公有云 CDN 通常不提供直接写 Nginx Lua 的权限,需在控制台找到“边缘脚本”或“函数计算”功能。不同厂商语法存在差异,以下是阿里云 EdgeScript 的等效逻辑示例,具体 API 请以厂商最新文档为准。

# 阿里云 EdgeScript 示例 (语法与标准 Lua 不同)
$token = $arg_token
$expire = $arg_expire
$uri = $uri

# 参数存在性检查
if eq($token, '') {
    exit(403)
}

# 时间过期检查 (边缘脚本有特定时间函数)
if gt($msec, $expire) {
    exit(403)
}

# 签名校验需调用厂商提供的加密原语
# $sign = hmac_sha1($secret, $expire . ":" . $uri)
# if ne($token, $sign) { exit(403) }

# 注意:公有云沙箱环境通常不支持 socket、文件 IO 等标准库

在腾讯云 ESA 或 AWS CloudFront Functions 中,逻辑类似但语法可能是 JavaScript 或特定 DSL,务必在控制台沙箱中先行验证语法兼容性。

验证与日志排查

配置完成后,需通过以下步骤验证鉴权是否生效且不影响正常业务:

1. 正常请求测试:

如何在 CDN 边缘节点配置 Lua 脚本实现鉴权?
curl -i "http://example.com/test.png?expire=1715000000&token=正确的签名值"

预期返回 200 OK,且响应头中可能包含边缘节点标识(如 X-Cache 命中状态)。

2. 异常请求测试:

# 测试无 Token
curl -i "http://example.com/test.png"

# 测试过期 Token
curl -i "http://example.com/test.png?expire=1000&token=xxx"

# 测试错误签名
curl -i "http://example.com/test.png?expire=1715000000&token=wrong_sign"

预期均直接返回 403 Forbidden,且请求不应出现在源站访问日志中。

3. 查看边缘日志:大多数 CDN 支持实时日志推送。检查日志中是否有脚本执行相关的字段,确认鉴权失败的理由是“签名无效”还是“过期”,以便调整算法。

常见风险与规避

1. 时间同步问题:边缘节点时间与客户端时间不一致会导致签名校验失败。建议服务端生成 Token 时允许一定的时间误差窗口(如前后 5 分钟),脚本校验时不要精确到秒。

如何在 CDN 边缘节点配置 Lua 脚本实现鉴权?

2. 缓存污染:如果开启了缓存,务必将鉴权参数(如 token、expire)排除在缓存 Key 之外,否则可能导致一个用户的鉴权结果被缓存给另一个用户。在 Nginx 中可通过 proxy_cache_key 调整,公有云需在缓存配置中忽略特定参数。

3. 语法兼容性:公有云的“边缘脚本”往往是 Lua 的子集或沙箱版本,不支持所有标准库(如 socket、文件 IO)。编写前务必查阅厂商支持的 API 列表,避免使用 require 加载未授权的模块。

4. 性能损耗:虽然 Lua 很快,但复杂的加密运算仍会增加边缘节点延迟。避免在脚本中进行外部 HTTP 请求或数据库查询,所有校验应在本地完成。

5. 回滚方案:在 CDN 控制台保留上一版本的脚本配置,或准备一键关闭脚本功能的开关。一旦脚本出现语法错误导致全站 500,能立即切回直连源站或关闭鉴权。