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

求助:Twilio Authy推送通知Webhook的C#验证实现方案

我之前在集成Twilio Authy Webhook验证的时候也踩过同样的坑——官方文档只有Node.js和Ruby示例,转C#的时候因为字符编码、请求体读取这些细节卡了好久。好在最终整理出了一套能在生产环境稳定运行的验证逻辑,分享给你:

完整的C#验证实现(ASP.NET Core)

这个实现包含了所有关键的安全细节,比如常量时间比较防止时序攻击,严格读取原始请求体避免签名不匹配:

using System;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;

public static class AuthyWebhookValidator
{
    /// <summary>
    /// 验证Twilio Authy Webhook的X-Authy-Signature签名
    /// </summary>
    /// <param name="request">当前HTTP请求</param>
    /// <param name="authyApiKey">你的Authy API密钥</param>
    /// <returns>验证是否通过</returns>
    public static async Task<bool> ValidateSignatureAsync(HttpRequest request, string authyApiKey)
    {
        // 1. 检查请求头是否包含签名
        if (!request.Headers.TryGetValue("X-Authy-Signature", out var signatureHeader))
        {
            return false;
        }

        string receivedSignature = signatureHeader.ToString();

        // 2. 读取原始请求体(必须是未被解析的原始内容,不能用Request.Form/Request.BodyReader的已处理数据)
        request.Body.Position = 0; // 重置流指针,确保从头读取
        using var streamReader = new StreamReader(request.Body, Encoding.UTF8);
        string rawRequestBody = await streamReader.ReadToEndAsync();
        request.Body.Position = 0; // 重置指针,让后续中间件能正常读取

        // 3. 生成HMAC-SHA256哈希签名
        byte[] keyBytes = Encoding.UTF8.GetBytes(authyApiKey);
        byte[] bodyBytes = Encoding.UTF8.GetBytes(rawRequestBody);

        using var hmacSha256 = new HMACSHA256(keyBytes);
        byte[] computedHash = hmacSha256.ComputeHash(bodyBytes);

        // 4. 转换为Twilio要求的小写十六进制格式
        string computedSignature = BitConverter.ToString(computedHash)
            .Replace("-", string.Empty)
            .ToLowerInvariant();

        // 5. 常量时间对比,防止时序攻击
        return CryptographicOperations.FixedTimeEquals(
            Encoding.UTF8.GetBytes(computedSignature),
            Encoding.UTF8.GetBytes(receivedSignature));
    }
}

在控制器中的使用示例

把验证逻辑集成到你的Webhook控制器里,确保在处理业务逻辑前先验证签名:

[ApiController]
[Route("webhooks/authy")]
public class AuthyWebhookController : ControllerBase
{
    private readonly IConfiguration _config;

    public AuthyWebhookController(IConfiguration config)
    {
        _config = config;
    }

    [HttpPost]
    public async Task<IActionResult> ProcessWebhook()
    {
        string authyApiKey = _config["Authy:ApiKey"]; // 从配置/密钥管理服务读取密钥

        // 执行签名验证
        bool isSignatureValid = await AuthyWebhookValidator.ValidateSignatureAsync(Request, authyApiKey);
        if (!isSignatureValid)
        {
            return Unauthorized("Invalid Authy webhook signature");
        }

        // 这里处理Webhook的业务逻辑
        // ...

        return Ok();
    }
}

关键注意事项

  • 原始请求体的重要性:一定要读取未被解析的原始请求体,不能用Request.Form或者已被Json序列化的内容——Twilio是基于原始POST内容生成签名的,任何解析后的格式变化都会导致签名不匹配。
  • 密钥安全:绝对不要把Authy API密钥硬编码在代码里,用环境变量、Azure Key Vault或者AWS Secrets Manager这类工具存储和读取。
  • 编码一致性:全程使用UTF-8编码,和Twilio的实现保持一致,避免因编码差异导致签名失败。
  • 时序攻击防护:用CryptographicOperations.FixedTimeEquals代替普通的字符串相等判断,防止攻击者通过时序差异猜测签名。

我这套代码已经在生产环境跑了快一年,没出过签名验证的问题,你可以放心用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 06:33:15