Hashing de Segredo + Payload para SSO

Estou tentando seguir o guia oficial de SSO. Estou usando Golang no meu servidor e estou tentando corresponder o hash HMAC-SHA256 do payload + secret ao sig, mas estou obtendo valores diferentes. Como vocês estão fazendo a geração do hash? Vocês estão decompondo o payload para o conteúdo bruto, sem assinatura, e re-hashando?

Como é o seu código?

Você codificou o payload em base64?

E você acertou a ordem dos parâmetros? Diferentes linguagens/bibliotecas têm ordens de parâmetros diferentes (chave, mensagem) ou (mensagem, chave). É isso que me confunde toda vez.

1 curtida

Após analisar o payload, parece que ele vem em base64 por padrão, mas o hash 256SHA do payload codificado em base64 não está correto. Estou usando o segredo e a URL de exemplo do post original.

O código a seguir gera a seguinte saída:

 payloadURL = bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=

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

Meu código:

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

	// pegar o payload e o sig da query da 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)

	// obter o payload bruto a partir da versão 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 está codificado em hex, então codifique o hash do payload em hex para podermos comparar.
	//hashAsString := hex.EncodeToString(hash.Sum(nil))

	// comparar o hash codificado em hex do payload com o sig codificado em hex
	if strings.Compare(payload256, sig) != 0 {
		log.Fatal(err)
	}

Atualização: Corrigi isso após codificar meu hash HMAC-SHA256 resultante em hex (a assinatura também está codificada em hex). Veja também este post que diz que os exemplos no guia SSO estão incorretos. Então, alterei este código:

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)
}

De onde vem o payload256?

Desculpe, atualizei o OP com um código mais limpo.

O que é esse %0A fazendo isso logo antes de &sig? Isso é uma quebra de linha codificada em URL… é por isso que há uma linha em branco na sua saída de depuração após você imprimir o payloadURL; ela ainda está anexada no final.

1 curtida

Ótima observação! Corrigi isso aqui adicionando o caractere de nova linha aos parâmetros do .Trim():

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

Então, minha saída agora é:

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

Você deve verificar de onde a quebra de linha está vindo exatamente e evitar que ela seja adicionada.

Além disso, o payload deve ter mais pares nome=valor além do nonce?

1 curtida

Isso é apenas o conteúdo de exemplo do guia de SSO do Discourse, então não consigo dizer de onde vem a quebra de linha. Aparentemente, o guia está incorreto (de acordo com esta postagem), e parece ter havido um erro de copiar e colar no exemplo.

Talvez haja um pouco demais de copiar e colar de diferentes versões.

Mas, olhando para sua postagem logo acima desta, seu payloadURL não está codificado em URL e o payload256 não está codificado em hexadecimal, mas sim em base64.

Talvez você possa republicar seu código atual e sua saída atual.

1 curtida