2015-12-10 3 views
4

У меня возникли проблемы с успешной проверкой запроса на webhook от Trello. Вот что я знаю.Подтверждение подписки Trello Webhook

webhook документация Trello в here состояния:

Каждый триггер webhook содержит заголовок HTTP X-Trello-Webhook. Заголовок представляет собой дайджест base64 хеша HMAC-SHA1. Хешированный контент является конкатенацией тела полного запроса и callbackURL точно так же, как он был предоставлен во время создания веб-камеры. Ключ, используемый для подписи этого текста, является секретом вашего приложения.

Это понятно. Далее они говорят:

Из-за определенных значений по умолчанию в криптовых утилях в узле полезные значения, которые мы подписываем, рассматриваются как двоичные строки, а не utf-8. Например, если вы берете символ en-dash (U + 2013 или 8211 в десятичной форме) и создаете из него двоичный буфер в узле, он будет отображаться в виде буфера [19], которые являются 8 наименее значимыми бит 8211. Это значение, которое используется в дайджесте для вычисления SHA-1.

Это менее понятно для меня. Я понимаю, что каждый символ полезной нагрузки (body + callbackURL) был помещен в 8-битное целое число с игнорированием переполнения. (Потому что 8211 == 0b10000000010011 и 0b00010011 == 19) Вот где я думаю, что моя проблема.

Функции Я использую для размещения узла вопроса полезной нагрузки Trello является:

func bitShift(s string) []byte { 
    var byteString []byte 

    // For each rune in the string 
    for _, c := range s { 

     // Create a byte slice 
     b := []byte(string(c)) 

     // Take the sign off the least significant byte 
     tmp := b[len(b)-1] << 1 
     tmp = tmp >> 1 

     // Append it to the byte string 
     byteString = append(byteString, tmp) 
    } 
    return byteString 
} 

Кроме того, очень возможно, что я делаю что-то неправильно с основной стадией проверки. Мне все хорошо, хотя я немного новичок в этом.

// VerifyNotificationHeader ... 
func VerifyNotificationHeader(signedHeader, trelloAPISecret string, requestURL *url.URL, body []byte) bool { 

    // Put callbackURL and body into byte slice 
    urlBytes := bitShift(requestURL.String()) 
    bitBody := bitShift(string(body)) 

    // Sign, hash, and encode the payload 
    secret := []byte(trelloAPISecret) 
    keyHMAC := hmac.New(sha1.New, secret) 
    keyHMAC.Write(append(bitBody, urlBytes...)) 
    signedHMAC := keyHMAC.Sum(nil) 
    base64signedHMAC := base64.StdEncoding.EncodeToString(signedHMAC) 

    if comp := strings.EqualFold(base64signedHMAC, signedHeader); !comp { 
     return false 
    } 
    return true 
} 

Сообщите мне, если вам нужна дополнительная информация. Спасибо!

Обновление: Это решение, ознакомьтесь с ответами.

+0

У вас есть пример запроса и подписи, которые мы могли бы сопоставить? – JimB

+0

Спасибо за интерес! Если я не получу эту работу на следующий день или два, я добавлю всю информацию, необходимую для проверки этих функций. На данный момент я неохотно создаю новую учетную запись Trello с единственной целью разделить секрет здесь. – doykle

ответ

2

Почему вы выбрасываете MSB? Вы конвертируете каждый rune в byte, который является безликим (и фактически является псевдонимом для uint8), так что бит содержит информацию, которую вы теряете.

Вы могли бы рассмотреть возможность использования функции, как это вместо:

func ascii(s string) []byte { 
    var ret []byte 
    for _, r := range s { 
     ret = append(ret, byte(r)) 
    } 
    return ret 
} 

Поскольку rune является псевдонимом для int32, отлитый в byte просто сбрасывает верхние 24 бита, который является то, что вы хотите.

(Оговорка: это предполагает, что мало-порядком байтов.)

+0

Я пытался сделать что-то вроде этого и получал ошибки переполнения, поэтому «bitShift» - это так. Это намного чище! Благодаря! Но я думаю, что они имеют одинаковый результат, не так ли? – doykle

+1

На en-dash, да, потому что бит 7 (с нулевой индексацией) равен нулю, но не для чего-либо с этим набором бит. 'bitShift' выкидывает это, но' ascii' сохраняет его. Например, 'bitShift (0b10001110)' дает '0b00001110', где' ascii (0b10001110) 'просто возвращает вам байт без изменений:' 0b10001110'. – mammothbane

+0

[пример игровой площадки] (https://play.golang.org/p/gUbmmkvRH-) – mammothbane

1

Существовали две проблемы с моим кодом. Основная проблема заключалась в том, что я использовал requestURL.String() для callbackURL.

В comments выше http.Request.URL:

Для большинства запросов, полей, кроме Пути и RawQuery будет пустым.

Оказалось, что requestURL.String() лишь давая [Path] часть [Scheme]://[Host][Path]. Правильный CallBackURL является

callbackURL := "https://" + request.Host + request.URL.String() 

второй проблема отмечались в this answer, где проверка провалилась бы для любого запроса, чье тело содержал 8--е-бит-имеющие символы.

Смежные вопросы