Hashing de secreto + carga útil para SSO

Estoy intentando seguir la guía oficial de SSO. Estoy usando Golang en mi servidor y estoy tratando de igualar el hash HMAC-SHA256 del payload + secret con la sig, pero estoy obteniendo valores diferentes. ¿Cómo están realizando el hash? ¿Están descomponiendo el payload en el contenido sin firmar y volviendo a generar el hash?

¿Cómo se ve tu código?

¿Codificaste la carga útil en base64?

¿Y obtuviste el orden de los parámetros correcto? Diferentes lenguajes o bibliotecas tienen diferentes órdenes de parámetros (clave, mensaje) o (mensaje, clave). Eso es lo que me confunde cada vez.

1 me gusta

Después de analizar el payload, parece que por defecto está en base64, pero el hash SHA256 del payload codificado en base64 no es correcto. Estoy utilizando el secreto y la URL de ejemplo del post original.

El siguiente código me da la siguiente salida:

 payloadURL = bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI=

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

Mi código:

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

	// obtener el payload y el sig de la consulta de la 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)

	// obtener el payload sin procesar desde la versión 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 en hexadecimal, así que codifica en hexadecimal el payload hasheado para poder compararlo.
	//hashAsString := hex.EncodeToString(hash.Sum(nil))

	// compara el hash codificado en hexadecimal del payload con el sig codificado en hexadecimal
	if strings.Compare(payload256, sig) != 0 {
		log.Fatal(err)
	}

Actualización: Lo solucioné después de codificar en hexadecimal mi hash resultante HMAC-SHA256 (la firma también está codificada en hexadecimal). También consulta este post que indica que los ejemplos en la guía de SSO son incorrectos. Así que cambié 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 dónde proviene payload256?

Lo siento, he actualizado el OP con un código más limpio.

¿Qué hace ese %0A justo antes de &sig? Es un salto de línea codificado en URL… por eso hay una línea en blanco en tu salida de depuración después de imprimir payloadURL, ya que todavía está añadida al final.

1 me gusta

¡Buen hallazgo! Lo solucioné aquí agregando el carácter de nueva línea a los parámetros de .Trim():

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

Así que mi salida ahora es:

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

Deberías revisar de dónde proviene realmente el salto de línea y evitar que se agregue.

Además, ¿el payload debería tener más pares nombre=valor además del nonce?

1 me gusta

Esto es solo el contenido de ejemplo de la guía de SSO de Discourse, por lo que no puedo determinar de dónde proviene el salto de línea. Aparentemente, la guía es incorrecta (según este post), y parece que hubo un error de copiar y pegar en el ejemplo.

Quizás hay un poco demasiado de copiar y pegar de diferentes versiones.

Pero al ver tu publicación justo encima de esta, tu payloadURL no está codificado en URL y payload256 no está codificado en hexadecimal, sino en base64.

¿Podrías volver a publicar tu código actual y tu salida actual?

1 me gusta