SSOのためのハッシュ化されたシークレットとペイロード

公式の SSO ガイド に従おうとしています。サーバー側では Golang を使用しており、payloadsecret の HMAC-SHA256 ハッシュ値を sig と一致させようとしていますが、値が一致しません。どのようにハッシュ処理を行っていますか?署名前の生のコンテンツにペイロードを分解して再ハッシュしていますか?

コードはどのようになっていますか?

ペイロードを base64 エンコードしましたか?

パラメータの順序は正しかったですか?言語やライブラリによって、(key, message) または (message, key) のようにパラメータの順序が異なります。これが私を毎回惑わせる要因です。

「いいね!」 1

payload をパースすると、デフォルトで base64 形式のようですが、base64 エンコードされた payload の 256SHA ハッシュ値が正しくありません。OG の投稿に記載されている例のシークレットと URL を使用しています。

以下のコードを実行すると、次の出力が得られます:

 payloadURL = bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=

sig = 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
payloadRaw = nonce=cb68251eefb5211e58c00ff1395f0c0b
payload256 = KCiqKYmXIrNaLxkdNO+bPOaV4Obu7EfetG1YjXDHy1Y=

私のコード:

testURL := "http://www.example.com/discourse/sso?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A\u0026sig=2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56"	
ssoSecret := "d836444a9e4084d5b224a60c208dce14"

// URL クエリから payload と sig を取得
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)

// base64 版から生の payload を取得
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 は hex エンコードされているため、ハッシュ化された payload も hex エンコードして比較可能にする。
//hashAsString := hex.EncodeToString(hash.Sum(nil))

// payload の hex エンコードされたハッシュと sig の hex エンコードされた値を比較
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 はどこから来るのですか?

申し訳ありませんが、OP をよりクリーンなコードに更新しました。

その \u0026sig の直前にある %0A は何でしょうか?あれは URL エンコードされた改行コードです。そのため、payloadURL を出力した後にデバッグ出力に空白行が残っているのです。まだ末尾に付いたままになっています。

「いいね!」 1

見事な発見です!.Trim() のパラメータに改行文字を追加することで、ここで修正しました。

payloadURL := strings.Trim(fmt.Sprint(q["sso"]), "[]\n")

これで出力は以下のようになりました。

payloadURL = bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=
sig = 2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56
payloadRaw = nonce=cb68251eefb5211e58c00ff1395f0c0b
payload256 = HOFJT5RIS29qCSvpsVzMHNr7H4Rgo4OPuw4Ig8Q5BHE=

改行が実際にどこから発生しているかを確認し、それが追加されないようにすべきです。

また、ペイロードには nonce 以外の name=value ペアももっと含まれているべきではありませんか?

「いいね!」 1

これは Discourse SSO ガイドの単なる例示なので、改行の由来がわかりません。どうやらガイドが誤っているようです(この投稿 によると)、例示にコピー&ペーストのミスがあったようです。

もしかすると、異なるバージョンからのコピペが少し多すぎるのかもしれません。

ただし、そのすぐ上の投稿を見ると、payloadURL は URL エンコードされておらず、payload256 は hex エンコードではなく base64 エンコードされています。

現在のコードと現在の出力を再投稿していただけませんか。

「いいね!」 1