You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

VCL中HMAC URL认证:如何提取不含hmac参数的URL部分

解决VCL中HMAC认证的URL参数解析问题

看起来你已经搞定了HMAC验证的核心逻辑,卡在了剥离URL中的hmac参数这一步,而且VCL里的变量用法可能踩了坑——我来帮你梳理下正确的实现方式:

一、先搞清楚VCL变量的正确用法

你之前用局部变量报错,大概率是因为VCL不支持自定义无作用域的局部变量,临时值要存在req.http.*这类请求头字段里(相当于临时存储容器),别直接用set $foo = ...这种写法。

二、提取HMAC参数值

直接通过VCL内置的查询参数访问器就能拿到,还要先做存在性检查:

# 检查是否存在hmac参数
if (!req.url.query.hmac) {
    return (synth(401, "Missing required HMAC parameter"));
}
# 提取hmac值到临时请求头
set req.http.X-Received-HMAC = req.url.query.hmac;

三、生成不含hmac的待签名查询字符串(关键!)

因为HMAC签名对参数顺序、格式非常敏感,推荐用遍历拼接的方式(比正则更可靠,避免边界情况):

# 初始化临时存储字段
set req.http.X-Signature-Data = "";

# 遍历所有查询参数,跳过hmac
foreach (var param in req.url.query) {
    if (param.name != "hmac") {
        # 如果不是第一个参数,先加&分隔符
        if (req.http.X-Signature-Data != "") {
            set req.http.X-Signature-Data = req.http.X-Signature-Data "&";
        }
        # 拼接参数名和值
        set req.http.X-Signature-Data = req.http.X-Signature-Data param.name "=" param.value;
    }
}

如果你的签名逻辑需要包含URL路径(比如/zzz/?q1=xxx&q2=yyy这种完整路径+查询串),就把路径也拼进去:

set req.http.X-Signature-Data = req.url.path "?" req.http.X-Signature-Data;

四、完整的HMAC验证逻辑

把上面的步骤整合起来,就是你要的验证流程:

sub vcl_recv {
    # 1. 检查并提取hmac参数
    if (!req.url.query.hmac) {
        return (synth(401, "Missing HMAC parameter"));
    }
    set req.http.X-Received-HMAC = req.url.query.hmac;

    # 2. 生成待签名的原始数据
    set req.http.X-Signature-Data = "";
    foreach (var param in req.url.query) {
        if (param.name != "hmac") {
            if (req.http.X-Signature-Data != "") {
                set req.http.X-Signature-Data = req.http.X-Signature-Data "&";
            }
            set req.http.X-Signature-Data = req.http.X-Signature-Data param.name "=" param.value;
        }
    }
    # 如果签名包含路径,取消下面注释
    # set req.http.X-Signature-Data = req.url.path "?" req.http.X-Signature-Data;

    # 3. 计算预期HMAC并验证
    set req.http.X-Expected-HMAC = digest.hmac_md5("your-secret-key-here", req.http.X-Signature-Data);
    if (req.http.X-Expected-HMAC != req.http.X-Received-HMAC) {
        return (synth(401, "Invalid HMAC. Expected: " req.http.X-Expected-HMAC " | Received: " req.http.X-Received-HMAC));
    }

    # 验证通过,继续处理请求
    # ...
}

补充说明

  • 为什么不用正则?比如std.regsub(req.url.query, "&?hmac=[^&]*", "", ...),虽然能快速替换,但如果hmac是第一个参数,替换后会留下开头的&,还要额外处理;而且如果参数顺序和签名生成时不一致,正则替换后的结果可能导致验证失败——遍历拼接能严格保留原参数顺序,更可靠。
  • 记得把your-secret-key-here替换成你实际的密钥,密钥要保密,别硬编码在VCL里(可以用对应CDN平台的KV存储或者环境变量注入)。

内容的提问来源于stack exchange,提问作者jsp

火山引擎 最新活动