DiscourseConnect 페이로드 해시 인코딩 불일치

안녕하세요,

저는 Discourse와 AWS를 처음 다루고 있어서 도움을 주시거나 올바른 방향으로 안내해 주실 분이 있으면 감사하겠습니다. 이 포럼의 지침https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045/1에 따라 제 웹사이트에 DiscourseConnect를 설정하려고 시도하고 있습니다.

실제 예제에서 base64 인코딩된 페이로드를 해시 처리하면 서명과 값이 다릅니다.
페이로드: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI= (줄바꿈 제외)
서명: 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
제 HMAC 해시(devglan 온라인 도구로 생성): 1ce1494f94484b6f6a092be9b15ccc1cdafb1f8460a3838fbb0e0883c4390471

리턴 URL에 대한 서명을 생성할 때는 웹사이트와 동일한 결과를 얻습니다. 그래서 결과가 일치하지 않는 이유가 정확히 무엇인지 모르겠습니다. 이 현상에 대한 통찰을 주실 수 있다면 매우 감사하겠습니다.

예시 페이로드, 얻은 해시 값, 그리고 시크릿을 공유해 주시겠어요? (여기에 게시하기 전에 테스트 사이트에서 시크릿을 변경하는 것을 잊지 마세요)

줄바꿈은 중요하며 서명에 영향을 미칩니다. Generate HMAC Online - SHA-256, SHA-384, SHA-512 도구는 해시를 계산하기 전에 줄바꿈을 제거하는 것으로 보입니다. https://www.freeformatter.com/hmac-generator.html 와 같은 다른 도구를 사용하면 더 좋은 결과를 얻을 수 있을 것입니다.

또한, URL 인코딩된 base64 페이로드의 HMAC을 계산해야 합니다. 따라서 원본 줄바꿈이 포함된 페이로드의 해시를 계산해서는 안 됩니다.

대신 %0A를 사용해야 합니다. 웹 프레임워크를 사용 중이라면, 프레임워크가 페이로드를 자동으로 디코딩했을 수 있습니다. 이를 비활성화하거나 값을 다시 인코딩하는 방법을 찾아야 합니다.

아직 내 웹사이트에서 이 내용을 테스트하지 않았습니다. 테스트하기 전에 수동으로 단계를 파악해 보고 있습니다.

앞서 언급된 포럼 게시글에서:

다음 설정이 주어졌을 때:
Discourse 도메인: http://discuss.example.com
DiscourseConnect URL: http://www.example.com/discourse/sso
DiscourseConnect 시크릿: d836444a9e4084d5b224a60c208dce14

사용자가 로그인 시도

  • Nonce 생성: cb68251eefb5211e58c00ff1395f0c0b
  • 원시 페이로드(Raw payload) 생성: nonce=cb68251eefb5211e58c00ff1395f0c0b
  • 페이로드 Base64 인코딩: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=\n
  • 페이로드 URL 인코딩: bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A
  • Base64 인코딩된 페이로드에 대해 HMAC-SHA256 생성: 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56

여기서 제가 다른 결과를 얻고 있습니다. 제안해 주신 HMAC 인코더를 Base64 인코딩되었으나 URL 인코딩되지 않은 페이로드에 적용하면 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56 대신 d26d5adf900de48890a0c3dcdeec108acd91b44a4b76c90c59955a5ba7b957f7가 나오고, URL 인코딩된 페이로드에 적용하면 46e749cd26dcabc84eed323ff31f830da674dc87c77a2fcb1b296f76402ea900가 나옵니다.

그러나 튜토리얼 후반부, 새 페이로드 생성 과정에서:

서명되지 않은 페이로드 생성:
nonce=cb68251eefb5211e58c00ff1395f0c0b&name=sam&username=samsam&email=test%40test.com&external_id=hello123&require_activation=true
(순서는 중요하지 않으며, 값은 URL 인코딩됨)

페이로드 Base64 인코딩
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ==

페이로드 URL 인코딩
bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1zYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRlcm5hbF9pZD1oZWxsbzEyMyZyZXF1aXJlX2FjdGl2YXRpb249dHJ1ZQ%3D%3D

Base64 인코딩된 페이로드 서명
3d7e5ac755a87ae3ccf90272644ed2207984db03cf020377c8b92ff51be3abc3

이 서명은 Base64 인코딩되었으나 URL 인코딩되지 않은 페이로드를 해시하여 생성되므로, 첫 번째 페이로드에서는 왜 작동하지 않는지 조금 혼란스럽습니다.

흥미롭네요! 저도 그 온라인 생성기를 사용해서 서명을 재현할 수 없었습니다. 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로 작성하고 RunKit에서 테스트해 보았을 때(nodeForge, urlencode, js-base64 라이브러리 사용) 튜토리얼과 동일한 결과가 반환되었습니다.