Несоответствие кодировки хэша полезной нагрузки DiscourseConnect

Здравствуйте,

Я новичок в работе с Discourse и AWS, поэтому надеюсь, что кто-то сможет помочь или подсказать правильное направление. Я пытаюсь настроить DiscourseConnect для своего веб-сайта, следуя инструкциям на этом форуме https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045/1.

В реальном примере, когда зашифрованный payload в формате base64 хешируется, я получаю значение, отличное от подписи.
Payload: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI= (перенос строки опущен)
Подпись: 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
Мой HMAC-хеш (сгенерирован с помощью онлайн-инструментов devglan): 1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471

Когда я создаю подпись для возвращаемого URL, результат совпадает с тем, что показывает веб-сайт, поэтому я не совсем понимаю, почему значения не совпадают. Если вы сможете предоставить какие-либо пояснения относительно причины этого, это было бы очень appreciated.

Можете ли вы привести пример полезной нагрузки, хеш, который вы получаете, и секретный ключ? (обязательно измените секрет на вашем тестовом сайте перед публикацией здесь)

Переносы строк важны и влияют на подпись. Похоже, что HMAC-SHA256 Generator Online удаляет переносы строк перед вычислением хеша. Возможно, вам повезет больше с другим инструментом, например Free Online HMAC Generator / Checker Tool (MD5, SHA-256, SHA-512) - FreeFormatter.com

Кроме того, вам нужно вычислить HMAC для URL-закодированной base64-полезной нагрузки. Поэтому вы никогда не должны вычислять хеш полезной нагрузки, содержащей необработанный перенос строки.

Вместо этого следует использовать %0A. Если вы используете веб-фреймворк, имейте в виду, что он может автоматически декодировать полезную нагрузку. Вам потребуется найти способ отключить это или перекодировать значение.

Я ещё не тестировал это на своём сайте, сначала пытаюсь вручную разобраться в шагах.

Из упомянутого ранее сообщения на форуме:

При следующих настройках:
Домен Discourse: http://discuss.example.com
URL DiscourseConnect: http://www.example.com/discourse/sso
Секрет DiscourseConnect: d836444a9e4084d5b224a60c208dce14

Попытка входа пользователя

  • Сгенерирован nonce: cb68251eefb5211e58c00ff1395f0c0b
  • Сгенерирован исходный payload: nonce=cb68251eefb5211e58c00ff1395f0c0b
  • Payload закодирован в Base64: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\n
  • Payload закодирован в URL: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A
  • Сгенерирован HMAC-SHA256 для payload в Base64: 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56

Здесь я получаю другой результат. Когда я использовал HMAC-кодер, который вы предложили, для payload в Base64 без URL-кодирования, я получил d26d5adf900de48890a0c3dcdeec108acd91b44a4b76c90c59955a5ba7b957f7 вместо 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56. Когда я использовал его для payload с URL-кодированием, я получил 46e749cd26dcabc84eed323ff31f830da674dc87c77a2fcb1b296f76402ea900.

Однако позже в руководстве, при создании нового payload:

Сгенерирован неподписанный payload:
nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123&require_activation=true
(порядок не важен, значения закодированы в URL)

Payload закодирован в Base64:
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ==

Payload закодирован в URL:
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D

Payload в Base64 подписан:
3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3

Эта подпись генерируется путём хеширования payload в Base64 без URL-кодирования, поэтому я немного не понимаю, почему это не работает для первого payload.

Интересно! Я тоже не смог воспроизвести подпись с помощью этого онлайн-генератора. В Ruby я могу сделать следующее, и это работает как ожидалось:

pry(main)> OpenSSL::HMAC.hexdigest("sha256", "d836444a9e4084d5b224a60c208dce14", "bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\n")
=> "2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56"

По наитию я попробовал сгенерировать подпись для строки с возвратом каретки и переводом строки в конце (то есть с переносами строк в стиле Windows) и сумел получить ту же подпись, что и в инструменте freeformatter.com:

pry(main)> OpenSSL::HMAC.hexdigest("sha256", "d836444a9e4084d5b224a60c208dce14", "bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\r\n")
=> "200c03f1e5d7b859170be102b436d74f761040261be9682b4afec67eb908fabf"

Так что, думаю, лучшее решение — избегать этих онлайн-инструментов и попробовать минимальную реализацию на том языке программирования, который вы планируете использовать.

Спасибо за ваше предложение! Я реализовал часть процесса на JavaScript и, протестировав его (с использованием библиотек nodeForge, urlencode и js-base64) в RunKit, получил тот же результат, что и в руководстве.