提问人:Christian Rößner 提问时间:9/11/2023 最后编辑:Christian Rößner 更新时间:9/12/2023 访问量:114
为什么 WebAuthn 服务器 (go) 和客户端 (JavaScript) 之间的质询验证失败?
Why does a challenge validation fail between WebAuthn server (go) and client (JavaScript)?
问:
我正在开发客户端服务器应用程序。服务器部分是用纯 Go 编写的,使用 go-webauthn/webauthn 库。客户端是纯 JavaScript。
目前,我已经实现了一个寄存器开始和寄存器结束钩子,它已经进行了一些通信。
当用户使用他/她的 Web 浏览器登录时,可以注册 webauthn 设备。通过单击对接,会出现一个对话框,我可以触摸我的 Yubikey 的传感器。之后,数据被发送到服务器,服务器解析数据并进行验证。不幸的是,这失败了!
由于我对 JavaScript 非常陌生,我花了好几个小时,这是我目前的草稿:
const register_device = async (event) => {
event.preventDefault();
const buffer_encode = array_buffer => {
return btoa(String.fromCharCode.apply(null, new Uint8Array(array_buffer)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
const buffer_decode = array_buffer => {
return Uint8Array.from(array_buffer, c => c.charCodeAt(0))
}
// Check whether current browser supports WebAuthn
if (!window.PublicKeyCredential) {
alert("Error: this browser does not support WebAuthn");
return;
}
let status_code
// The username is stored in a cookie. Start the registration process...
await fetch("/2fa/v1/webauthn/register/begin")
.then(async response => {
const credentialCreationOptions = await response.json();
// console.log(credentialCreationOptions.publicKey.challenge);
credentialCreationOptions.publicKey.challenge = buffer_decode(
credentialCreationOptions.publicKey.challenge
);
credentialCreationOptions.publicKey.user.id = buffer_decode(
credentialCreationOptions.publicKey.user.id
);
return navigator.credentials.create({
publicKey: credentialCreationOptions.publicKey
})
})
.then(async credential => {
let attestationObject = credential.response.attestationObject;
let clientDataJSON = credential.response.clientDataJSON;
let rawId = credential.rawId;
/*
// Decode the clientDataJSON into a utf-8 string
const utf8Decoder = new TextDecoder('utf-8');
const decodedClientData = utf8Decoder.decode(clientDataJSON);
// Parse the string as an object
const clientDataObj = JSON.parse(decodedClientData);
console.log(clientDataObj)
*/
return await fetch("/2fa/v1/webauthn/register/finish", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: credential.id,
rawId: buffer_encode(rawId),
type: credential.type,
response: {
attestationObject: buffer_encode(attestationObject),
clientDataJSON: buffer_encode(clientDataJSON),
}
})
})
})
.then (async response => {
status_code = response.status;
return await response.json()
})
.then (result => {
console.log(`Registration result: code=${status_code} message=${result}`);
})
.catch (error => {
console.error(`An error occurred when registering the user: ${error}`);
});
}
device.addEventListener("click", register_device);
我有一个最好的猜测:
我真的不知道buffer_encode和buffer_decode功能是什么。它确实在某种程度上起作用,但我认为它破坏了一些东西。
我不明白的是,为什么我在buffer_enocde例程中需要这些 .replace() 语句?如果我删除这些行,为什么它根本不起作用?
有没有人可以向我展示正确的buffer_encode和buffer_decode功能,以便 webauthn 的其余部分正常工作?
目前,服务器在结束钩子处返回验证失败。
response, err := protocol.ParseCredentialCreationResponseBody(ctx.Request.Body)
if err != nil {
ctx.JSON(http.StatusBadRequest, err.Error())
return
}
credential, err := webAuthn.CreateCredential(user, *sessionData, response)
if err != nil {
ctx.JSON(http.StatusBadRequest, err.Error())
return
}
I use go-webauthn/webauthn with go-gingonic.
CreateCredential() throws the error I am asking about.
Many thanks in advance.
答:
The challenges don't match. The second string () is a base64 encoded version of the first string ():cm9MYXU4cFhOaElyb21kQk5uNnMzUE9RT0ZBd2NtY1Y5czhQVjUzc2hSaw
roLau8pXNhIromdBNn6s3POQOFAwcmcV9s8PV53shRk
Don't double base64 encode the challenge.
评论