签名url必须包含严格校验的过期时间,服务端需用同一规则重算签名并拒绝超时或“永不过期”请求,参数须字典序拼接且filename需标准化编码,密钥严禁硬编码,中间件须在管道早期验证。

签名URL必须包含过期时间且服务端严格校验
临时签名URL不是加个哈希就完事,核心是时效性与服务端一致性。客户端生成的签名,服务端必须用同一套规则重新计算并比对,且必须拒绝已过期的 expires 参数值。常见错误是只校验签名正确,却放行 expires=2147483647 这类“永不过期”的伪造时间戳。
- 签名前先生成 UNIX 时间戳(秒级),例如
DateTimeOffset.UtcNow.ToUnixTimeSeconds(),设为expires参数值,有效期建议控制在 5–30 分钟 - 服务端收到请求后,第一步不是解密或验签,而是立即检查
expires是否 ≤ 当前时间戳,超时直接返回403 Forbidden - 签名原文必须固定排序拼接,推荐格式:
filename={filename}&expires={expires}&secret={sharedSecret},其中filename需 URL 解码后再参与计算,避免客户端双重编码绕过校验
C# 用 HMACSHA256 生成签名并拼装 URL
别用 MD5 或 SHA1,它们已被证实不安全;.NET 原生 HMACSHA256 足够快又合规。签名本质是「对关键参数做密钥化摘要」,不是加密,所以不需要解密环节。
- 共享密钥
sharedSecret必须存于配置(如appsettings.json的FileUpload:SigningKey),严禁硬编码或前端可见 - 拼接签名原文时,所有参数必须按字典序排列(
expires、filename、token等),否则服务端重算结果必然不一致 - 示例代码片段:
var expires = DateTimeOffset.UtcNow.AddMinutes(10).ToUnixTimeSeconds();
var message = $"filename={Uri.EscapeDataString(filename)}&expires={expires}";
var key = Encoding.UTF8.GetBytes(_config["FileUpload:SigningKey"]);
using var hmac = new HMACSHA256(key);
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(message)));
var signedUrl = $"/upload?filename={Uri.EscapeDataString(filename)}&expires={expires}&signature={Uri.EscapeDataString(signature)}";ASP.NET Core 中间件验证签名URL的三个硬性步骤
验证不能只靠一个 IActionFilter,必须在管道早期拦截——否则恶意请求可能触发文件系统操作或日志写入。中间件比 ActionFilter 更早执行,也更容易统一返回错误响应。
- 从
HttpContext.Request.Query提取filename、expires、signature,任一缺失立即context.Response.StatusCode = 400 - 将
expires解析为long,若失败或小于当前时间戳,返回403(注意:不用DateTimeOffset.FromUnixTimeSeconds再转回比较,直接比数字更安全) - 用相同密钥和相同拼接规则重算签名,
Convert.FromBase64String后用SequenceEqual比较字节数组,避免字符串比较引入时序攻击风险
前端传 filename 时 URL 编码不一致导致签名失效
浏览器对 URL 中的中文、空格、括号等字符自动编码,但不同环境(Chrome/Firefox/WebView)编码细节有差异;C# 后端若用 Request.Query["filename"] 直接取值,得到的是已解码结果,而签名原文里用的是原始未解码字符串——这就断了链。
- 前端上传前,对
filename显式调用encodeURIComponent,然后作为查询参数传入 - 后端签名验证时,拼接原文用的是
Uri.UnescapeDataString(filename)后再Uri.EscapeDataString标准化(即还原原始语义再统一编码),而不是直接拼原始 Query 值 - 测试时用含中文名文件(如
报告 v2(终稿).pdf)跑通全流程,比用test.pdf更能暴露编码问题
签名最脆弱的环节不在算法,而在时间同步、编码一致性、密钥管理这三个地方。哪怕 HMAC 再标准,只要服务器时钟慢了 2 分钟,或前端多套了一层 encodeURI,整个机制就形同虚设。










