iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
Security

Go!帶你探索 FIDO2 資安技術全端應用系列 第 29

【Go!帶你探索 FIDO2 資安技術全端應用】Day 29 - 開始進行前後端串接 (9)

  • 分享至 

  • xImage
  •  

昨天通過了第八關之後,今天要來接續第九關

串接之旅 Part 9

開始測試囉

串接第九關:failed to validate login, error: User does not own the credential returned

https://ithelp.ithome.com.tw/upload/images/20240924/20140363qTQVkLerJQ.png

從錯誤來看,是當前要進行 WebAuthn Authentication 的使用者,在後端沒有找到對應的 Credential 可以進行驗證

我們從 go-webauthn source code 來判斷有哪些可能性

https://ithelp.ithome.com.tw/upload/images/20240924/20140363FF5VyrSkLT.png

看起來是 protocol.CredentialAssertionResponse 中的 RawID 與 session 中的 allowCredentials 裡的 credential id 對不起來所導致的

那這個我們從後端進行修改

我們新增一個資料夾,叫做 utils,並在裡面新增一個檔案 base64.go
接著在裡面新增下面的程式碼

package utils

import (
	"encoding/base64"
	"strings"
)

func convertToBase64StdEncoding(base64URLEncoded string) string {
	base64StdEncoded := base64URLEncoded
	base64StdEncoded = strings.Replace(base64StdEncoded, "-", "+", -1)
	base64StdEncoded = strings.Replace(base64StdEncoded, "_", "/", -1)

	if len(base64StdEncoded)%4 != 0 {
		base64StdEncoded += strings.Repeat("=", 4-len(base64StdEncoded)%4)
	}

	return base64StdEncoded
}

func DecodeToBase64StdEncoding(src string) ([]byte, error) {
	base64StdEncoded := convertToBase64StdEncoding(src)
	base64StdDecoded, err := base64.StdEncoding.DecodeString(base64StdEncoded)

	return []byte(string(base64StdDecoded)), err
}

接下來再到 controller/assertion.go 中的 FinishAssertionHandler

我們將前端提供的 Credential ID 使用上面寫的 Function 轉換成 Credential Raw ID,像是下面這樣

credentialRawID, err := utils.DecodeToBase64StdEncoding(request.Id)
if err != nil {
    ctx.JSON(
        http.StatusBadRequest,
        api.CommonResponse{
            Status:       "failed",
            ErrorMessage: "failed to encode credential raw id, error: " + err.Error(),
        },
    )
    return
}

接著用轉換出來的 credentialRawID 取代原本在 protocol.CredentialAssertionResponse 中的 RawID

car := protocol.CredentialAssertionResponse{
    PublicKeyCredential: protocol.PublicKeyCredential{
        Credential: protocol.Credential{
            ID:   request.Id,
            Type: request.Type,
        },
        RawID:                  protocol.URLEncodedBase64(credentialRawID),
        ClientExtensionResults: request.GetClientExtensionResults,
    },
    AssertionResponse: protocol.AuthenticatorAssertionResponse{
        AuthenticatorResponse: protocol.AuthenticatorResponse{
            ClientDataJSON: protocol.URLEncodedBase64(authenticatorClientDataJSON),
        },
        AuthenticatorData: protocol.URLEncodedBase64(authenticatorData),
        Signature:         protocol.URLEncodedBase64(authenticatorSignature),
        UserHandle:        protocol.URLEncodedBase64(authenticatorUserHandle),
    },
}

再重新執行一次,就成功通過第九關了~但又遇到最終大魔王,第十關!

串接最終關:failed to validate login, error: userHandle and User ID does not match

https://ithelp.ithome.com.tw/upload/images/20240924/20140363PvVubDtfhB.png

從錯誤來看,感覺是 Authenticator 回傳的 UserHandle 跟 User ID 不吻合

那我們看一下 go-webauthn source code 來判斷有哪些可能性

https://ithelp.ithome.com.tw/upload/images/20240924/20140363qImYUANa2I.png

看起來有可能資料庫那邊實作的 WebAuthnID()protocol.CredentialAssertionResponse 裡的 UserHandle 不吻合
那我們看一下 protocol.CredentialAssertionResponse 裡的 UserHandle 是什麼

{"ID":"HeZ5B367bXiKOWqfrtC92_OePlg","Type":"public-key","rawId":"HeZ5B367bXiKOWqfrtC92/OePlg=","Response":{"CollectedClientData":{"type":"webauthn.get","challenge":"N3hrWk56WmFudHlnS2VxUlROWjZ2dDdiSXMyMkY4S0NQVW1xZ1BMNDF5MA","origin":"https://ab15-211-20-7-115.ngrok-free.app"},"AuthenticatorData":{"rpid":"XPS8nD4bj97jBqQcXhhmjwu+nLSR5/WOIRyKrL+1xR0=","flags":29,"sign_count":0,"att_data":{"aaguid":null,"credential_id":null,"public_key":null},"ext_data":null},"Signature":"MEUCIEs/427hEij5MNigWtUEZm4jFHB6DUMOF1pujUkqGTayAiEAwbfgAiOULh0NbxbDhqJjNNfeHEszK8qGeBv6cK5Mw54=","UserHandle":"bGVvaG8="},"Raw":{"id":"HeZ5B367bXiKOWqfrtC92_OePlg","type":"public-key","rawId":"HeZ5B367bXiKOWqfrtC92_OePlg","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTjNocldrNTZXbUZ1ZEhsblMyVnhVbFJPV2paMmREZGlTWE15TWtZNFMwTlFWVzF4WjFCTU5ERjVNQSIsIm9yaWdpbiI6Imh0dHBzOi8vYWIxNS0yMTEtMjAtNy0xMTUubmdyb2stZnJlZS5hcHAifQ","authenticatorData":"XPS8nD4bj97jBqQcXhhmjwu-nLSR5_WOIRyKrL-1xR0dAAAAAA","signature":"MEUCIEs_427hEij5MNigWtUEZm4jFHB6DUMOF1pujUkqGTayAiEAwbfgAiOULh0NbxbDhqJjNNfeHEszK8qGeBv6cK5Mw54","userHandle":"bGVvaG8"}}}

可以看到 UserHandle 是 bGVvaG8

而資料庫那邊的 WebAuthnID() 回傳的是 184e6301-6770-462b-8ee9-f0f50577914c
看來知道問題點在哪裡了

那我們嘗試將 UserHandle 進行 base64RawURL 解碼看看,可以看到會是跟我們輸入的 username 相同

userHandle := "bGVvaG8"
base64RawURLDecodedUserHandle, err := base64.RawURLEncoding.DecodeString(userHandle)
if err != nil {
    fmt.Println(err.Error())
}
fmt.Println(string(base64RawURLDecodedUserHandle))

// 輸出:leoho

所以我們可以將 WebAuthnID() 那邊修改成下面這樣

func (u *User) WebAuthnID() []byte {
	return []byte(u.Name)
}

再重新執行一次,就成功通過最終關了~

https://ithelp.ithome.com.tw/upload/images/20240924/20140363u7GoXpBUS5.png

成功完成 WebAuthn Authentication 了!


上一篇
【Go!帶你探索 FIDO2 資安技術全端應用】Day 28 - 開始進行前後端串接 (8)
下一篇
【Go!帶你探索 FIDO2 資安技術全端應用】Day 30 - 勇者鬥惡龍成功之結語!
系列文
Go!帶你探索 FIDO2 資安技術全端應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言