Я пытаюсь следовать официальному руководству по SSO. На своём сервере я использую Golang и пытаюсь сопоставить HMAC-SHA256-хэш от payload + secret с sig, но получаю разные значения. Как вы выполняете хеширование? Разбиваете ли вы payload на исходное, не подписанное содержимое и снова хешируете его?
Как выглядит ваш код?
Вы закодировали полезную нагрузку в base64?
И правильно ли вы указали порядок параметров? В разных языках и библиотеках порядок параметров разный: (ключ, сообщение) или (сообщение, ключ). Именно это каждый раз сбивает меня с толку.
После разбора payload выяснилось, что по умолчанию он в формате base64, но хеш 256SHA от закодированного в base64 payload неверен. Я использую секрет и URL из оригинального поста.
Следующий код выдаёт следующий вывод:
payloadURL = bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=
sig = 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
payloadRaw = nonce=cb68251eefb5211e58c00ff1395f0c0b
payload256 = KCiqKYmXIrNaLxkdNO+bPOaV4Obu7EfetG1YjXDHy1Y=
Мой код:
testURL := "http://www.example.com/discourse/sso?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A&sig=2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56"
ssoSecret := "d836444a9e4084d5b224a60c208dce14"
// извлекаем payload и sig из параметров URL
u, err := url.Parse(testURL)
if err != nil {
log.Fatal(err)
}
q := u.Query()
payloadURL := strings.Trim(fmt.Sprint(q["sso"]), "=[]")
sig := strings.Trim(fmt.Sprint(q["sig"]), "[]")
fmt.Printf("payloadURL = %s\n", payloadURL)
fmt.Printf("sig = %s\n", sig)
// получаем исходный payload из base64-версии
payloadRaw, err := base64.StdEncoding.DecodeString(payloadURL)
if err != nil {
log.Fatal(err)
}
fmt.Printf("payloadRaw = %s\n", payloadRaw)
key := []byte(ssoSecret)
message := []byte(payloadURL)
hash := hmac.New(sha256.New, key)
hash.Write(message)
payload256 := base64.StdEncoding.EncodeToString(hash.Sum(nil))
if err != nil {
log.Fatal(err)
}
fmt.Printf("payload256 = %s\n", payload256)
// sig закодирован в шестнадцатеричный формат, поэтому также закодируем хешированный payload в hex для сравнения.
//hashAsString := hex.EncodeToString(hash.Sum(nil))
// сравниваем hex-закодированный хеш payload с hex-закодированным sig
if strings.Compare(payload256, sig) != 0 {
log.Fatal(err)
}
Обновление: я исправил это, закодировав полученный хеш HMAC-SHA256 в hex (подпись также закодирована в hex). Также см. этот пост, где указано, что примеры в руководстве по SSO неверны. Поэтому я изменил этот код:
key := []byte(ssoSecret)
message := []byte(payloadURL)
hash := hmac.New(sha256.New, key)
hash.Write(message)
payload256 := hex.EncodeToString(hash.Sum(nil))
if err != nil {
log.Fatal(err)
}
Откуда берется payload256?
Извините, я обновил первый пост с более чистым кодом.
Что делает там этот %0A прямо перед &sig? Это URL-кодированный перевод строки… именно поэтому в вашем выводе отладки после печати payloadURL появляется пустая строка — он всё ещё прицеплен в конце.
Отличное замечание! Я исправил это здесь, добавив символ новой строки в параметры .Trim():
payloadURL := strings.Trim(fmt.Sprint(q["sso"]), "[]\n")
Теперь мой вывод выглядит так:
payloadURL = bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=
sig = 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
payloadRaw = nonce=cb68251eefb5211e58c00ff1395f0c0b
payload256 = HOFJT5RIS29qCSvpsVzMHNr7H4Rgo4OPuw4Ig8Q5BHE=
Вам стоит проверить, откуда именно берётся перенос строки, и предотвратить его добавление.
Кроме того, в полезной нагрузке должно быть больше пар name=value, чем просто nonce?
Это просто пример из руководства по SSO для Discourse, поэтому я не могу сказать, откуда взялся перенос строки. Судя по всему, в руководстве есть ошибка (согласно этому сообщению), и похоже, что в примере произошла ошибка копирования/вставки.
Возможно, вы слишком много копируете из разных версий.
Но, глядя на ваш пост прямо над этим, payloadURL не закодирован в URL, а payload256 закодирован не в шестнадцатеричном формате, а в base64.
Может быть, вы могли бы снова опубликовать ваш текущий код и текущий вывод.