You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Cloudflare Worker验证服务端Node生成的预签名URL

报错根因说明

你遇到的subtle of undefined报错是因为Cloudflare Worker运行在专属的边缘运行环境,不兼容Node.js的require('crypto')语法,Web Crypto API的subtle接口直接挂载在全局crypto对象下,无需额外引入,本地调试请使用官方wrangler工具,不要用Node.js直接运行Worker代码。

完整适配方案

这套方案只需要对服务端做极小的兼容调整,Worker侧即可完成签名验证,全程符合你原有签名逻辑,也支持对接带宽联盟中转源站存储。

1. 服务端调整(可选,仅为规避域名匹配问题)

将原有签名逻辑中传入的完整URL改为相对路径,避免源站域名和Cloudflare调度域名不一致导致签名校验失败,调整后代码如下:

const util = require('util');
const crypto = require('crypto');

function expireDate() {
    const d = new Date();
    d.setUTCHours(5, 0, 0, 0); // 5 am
    d.setUTCDate(d.getDate() + 2); // + 2 days
    return Math.floor(d.getTime() / 1000);
}

function generateSignedUrl(relativePath) {
    const expire_date = expireDate();
    const hmac = crypto.createHmac('sha256', 'mystrongkey');
    // 签名规则和之前完全一致,仅传入路径不带域名
    const signStr = util.format('%s\n%d\n%s', 'GET', expire_date, relativePath);
    const tempUrlSig = hmac.update(signStr).digest('hex');
    // 最终返回的预签名URL拼接你的Cloudflare调度域名
    return `https://你的Cloudflare绑定域名${relativePath}?temp_url_sig=${tempUrlSig}&temp_url_expires=${expire_date}`;
}

const presigned_url = generateSignedUrl('/test.jpg')

2. Cloudflare Worker 验证代码

将密钥mystrongkey配置为Worker的环境变量SECRET_KEY(不要硬编码在代码中),完整代码如下:

// 加载预设的密钥环境变量
const SECRET_KEY = new TextEncoder().encode(SECRET_KEY)

// 导入HMAC密钥
async function getHmacKey() {
  return crypto.subtle.importKey(
    "raw",
    SECRET_KEY,
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["verify"]
  )
}

// 签名验证函数
async function verifySignature(signStr, expectedSig) {
  const key = await getHmacKey()
  const data = new TextEncoder().encode(signStr)
  const sigBuf = new Uint8Array(expectedSig.match(/.{1,2}/g).map(byte => parseInt(byte, 16)))
  return crypto.subtle.verify("HMAC", key, sigBuf, data)
}

export default {
  async fetch(request) {
    const url = new URL(request.url)
    // 提取签名和过期时间参数
    const tempUrlSig = url.searchParams.get('temp_url_sig')
    const tempUrlExpires = url.searchParams.get('temp_url_expires')

    // 缺少参数直接返回403
    if (!tempUrlSig || !tempUrlExpires) {
      return new Response('Missing signature parameters', { status: 403 })
    }

    // 校验是否过期
    const now = Math.floor(Date.now() / 1000)
    if (now > parseInt(tempUrlExpires)) {
      return new Response('URL expired', { status: 403 })
    }

    // 按照和服务端完全一致的规则拼接待签名字符串
    const signStr = `GET\n${tempUrlExpires}\n${url.pathname}`

    // 验证签名
    const isValid = await verifySignature(signStr, tempUrlSig)
    if (!isValid) {
      return new Response('Invalid signature', { status: 403 })
    }

    // 验证通过,转发请求到你的源站对象存储,自动走带宽联盟链路
    const originUrl = new URL(url.pathname, 'https://你的源站存储域名')
    return fetch(originUrl.toString())
  }
}

额外说明

如果你需要直接验证AWS S3原生生成的预签名URL,只需要调整Worker侧的签名拼接规则,匹配AWS S3 V4签名的待签名字段即可,核心的Web Crypto调用逻辑无需修改。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 14:12:01