Невозможно заставить подпись Typeform Webhook с C# работать - PullRequest
0 голосов
/ 16 апреля 2020

Во-первых, этот вопрос был задан и дан ответ здесь , но он был указан c для Ruby / PHP, и хотя я пытался следовать ему и руководству самих Typeform, я не могу реализовать проверку Typeform-Signature в C#.

. Я написал метод расширения для проверки подписи Typeform в отношении полезной нагрузки, отправленной через webhook. Если подпись верна, она возвращает строку (json) полезной нагрузки, но если нет, то возвращает ошибку.

public static class HttpRequestExtensions {
    private const string SignatureHeader = "Typeform-Signature";
    private static readonly Encoding encoding = new UTF8Encoding ();

    public static async Task<Result<string>> ValidateAndRetrievePayload (this HttpRequestMessage request, string key) {
        var headerValue = request.GetHeaderValue (SignatureHeader);
        if (string.IsNullOrWhiteSpace (headerValue)) return Result.Failure<string> ($"'{SignatureHeader}' Header not found or empty.");

        var json = await request.Content.ReadAsStringAsync ();
        var payload = encoding.GetBytes (json);
        using (var hmac256 = new HMACSHA256 (encoding.GetBytes (key))) {
            var hashPayload = hmac256.ComputeHash (payload);
            var base64String = Convert.ToBase64String (hashPayload);
            var hashResult = $"sha256={base64String}";
            if (hashResult.Equals (headerValue)) return Result.Success (json);
            return Result.Failure<string> ($"'{SignatureHeader}' does not match. Header: `{headerValue}` | Hash: `{hashResult}`");
        }
    }
}

На основании других вопросов, найденных в SO, я изменил метод для запуска без кодирования ( см. ниже), но все равно с тем же результатом, хэши не совпадают.

public static class HttpRequestExtensions
{
    private const string SignatureHeader = "Typeform-Signature";

    public static async Task<Result<string>> ValidateAndRetrievePayload(this HttpRequestMessage request, string key)
    {
        var headerValue = request.GetHeaderValue(SignatureHeader);
        if (string.IsNullOrWhiteSpace(headerValue))
            return Result.Failure<string>($"'{SignatureHeader}' Header not found or empty.");

        var payload = await request.Content.ReadAsByteArrayAsync();
        var byteKey = GetBytes(key);
        using (var hmac256 = new HMACSHA256(byteKey))
        {
            var hashPayload = hmac256.ComputeHash(payload);
            var base64String = Convert.ToBase64String(hashPayload);
            var hashResult = $"sha256={base64String}";
            if (hashResult.Equals(headerValue))
                return Result.Success(await request.Content.ReadAsStringAsync());
            return Result.Failure<string>(
                $"'{SignatureHeader}' does not match. Header: `{headerValue}` | Hash: `{hashResult}`");
        }
    }

    private static byte[] GetBytes(string value)
    {
        var bytes = new byte[value.Length * sizeof(char)];
        Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }

    private static string GetString(byte[] bytes)
    {
        var chars = new char[bytes.Length / sizeof(char)];
        Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }
}

1 Ответ

0 голосов
/ 29 апреля 2020

Надеюсь, это поможет ... я тоже новичок в TypeForm API, но у меня есть следующий код, который работает ... очень грязно, я пока не нашел время для его рефакторинга ... он работает на одной из моих форм хорошо, но по какой-то причине, когда я использую другую учетную запись TypeForm, она не работает (хотя я и установил тот же секрет) .... вот почему я еще не реорганизовал ее ...

Но просто поделиться, потому что это работает для моей первой учетной записи и может быть полезным (моя вторая учетная запись имеет ту же проблему, где результат не совпадает ... если я выясню почему, я сообщу вам здесь и обновлю этот ответ

    private bool IsValid(string jsonRequest)
    {
        string typeFormSig = Request.Headers["Typeform-Signature"];
        string generatedSig = $"sha256={CreateToken(jsonRequest)}";
        _logger.LogInformation($"SIGNATURE STUFF: sec: {SECRET} typeform: {typeFormSig}  MyGen: {generatedSig}");
        return (typeFormSig == generatedSig);
    }

    private static string CreateToken(string message)
    {
        var encoding = new System.Text.ASCIIEncoding();
        byte[] keyByte = encoding.GetBytes(SECRET);
        byte[] messageBytes = encoding.GetBytes(message);
        using (var hmacsha256 = new HMACSHA256(keyByte))
        {
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashmessage);
        }
    }

И вот как я получаю строку json:

[HttpPost("")]
    public async Task<IActionResult> Receive()
    {
        using (var reader = new StreamReader(Request.Body))
        {
            string jsonRequest = await reader.ReadToEndAsync();
...