เว็บแอพนี้เป็นตัวอย่างสำหรับไลบรารี Webauthn Server ของฉัน (fxamacker/webauthn) รองรับการลงทะเบียน WebAuthn และการรับรองความถูกต้อง มันใช้ REST API ที่เสนอสำหรับเซิร์ฟเวอร์ FIDO2

WebAuthn (การตรวจสอบเว็บ) เป็นมาตรฐานเว็บ W3C สำหรับการรับรองความถูกต้องของผู้ใช้ในแอพและบริการบนเว็บ มันเป็นองค์ประกอบหลักของ FIDO2 ผู้สืบทอดของ FIDO U2F Protocol
การสาธิตนี้ไม่ได้มีไว้สำหรับการใช้งานเพราะมันถูกออกแบบมาให้เป็นตัวอย่าง
go get github.com/fxamacker/webauthn-demo
$ CERTS_DIR=[folder containing cert.pem and key.pem] docker-compose up
การสาธิต Webauthn ทำงานที่ https: // localhost: 8443 บนโฮสต์นักเทียบท่าของคุณ
$ docker-compose up
การสาธิต Webauthn ทำงานที่ https: // localhost: 8443 บนโฮสต์นักเทียบท่าของคุณ
กระบวนการลงทะเบียนประกอบด้วยสองขั้นตอน: สร้างตัวเลือกการสร้างข้อมูลรับรองและลงทะเบียนข้อมูลรับรอง ดู ignup.html, webauthn.register.js และ registration_handlers.go
สร้างตัวเลือกการสร้างข้อมูลรับรอง:
การร้องขอ /attestation/options เซิร์ฟเวอร์โดยการส่งคืนตัวเลือกการสร้างข้อมูลรับรอง (PublicKeyCredentialCreationOptions) ไปยังไคลเอนต์ จากนั้นไคลเอนต์จะใช้ตัวเลือกเหล่านั้นด้วย navigator.credentials.create() เพื่อสร้างข้อมูลรับรองใหม่
// Simplified `/attestation/options` handler from registration_handlers.go
func (s *server) handleAttestationOptions(w http.ResponseWriter, r *http.Request) {
// Get user from datastore by username.
u, _ := s.dataStore.getUser(r.Context(), optionsRequest.Username)
// Create PublicKeyCredentialCreationOptions using webauthn library.
creationOptions, _ := webauthn.NewAttestationOptions(s.webAuthnConfig, &webauthn.User{ID: u.UserID, Name: u.UserName, DisplayName: u.DisplayName, CredentialIDs: u.CredentialIDs})
// Save creationOptions and user info in session to verify new credential later.
session.Values[WebAuthnCreationOptions] = creationOptions
session.Values[UserSession] = &userSession{User: u}
// Write creationOptions to response.
}
ลงทะเบียนข้อมูลรับรอง:
เซิร์ฟเวอร์ตรวจสอบและลงทะเบียนข้อมูลรับรองใหม่ที่ได้รับผ่าน /attestation/result
// Simplified `/attestation/result` handler from registration_handlers.go
func (s *server) handleAttestationResult(w http.ResponseWriter, r *http.Request) {
// Get saved creationOptions and user info from session.
// Parse and verify credential in request body.
credentialAttestation, _ := webauthn.ParseAttestation(r.Body)
expected := &webauthn.AttestationExpectedData{
Origin: s.rpOrigin,
RPID: savedCreationOptions.RP.ID,
CredentialAlgs: credentialAlgs,
Challenge: base64.RawURLEncoding.EncodeToString(savedCreationOptions.Challenge),
UserVerification: savedCreationOptions.AuthenticatorSelection.UserVerification,
}
_, _, err = webauthn.VerifyAttestation(credentialAttestation, expected)
// Save user credential in datastore.
c := &credential{
CredentialID: credentialAttestation.RawID,
UserID: uSession.User.UserID,
Counter: credentialAttestation.AuthnData.Counter,
CoseKey: credentialAttestation.AuthnData.Credential.Raw,
}
err = s.dataStore.addUserCredential(r.Context(), uSession.User, c)
// Write "ok" response.
}
กระบวนการตรวจสอบความถูกต้องต้องใช้สองขั้นตอน: สร้างตัวเลือกคำขอข้อมูลรับรองและตรวจสอบข้อมูลรับรอง ดู signin.html, webauthn.authn.js และ Authentication_handlers.go
สร้างตัวเลือกคำขอข้อมูลรับรอง:
การร้องขอการจัดการ /assertion/options โดยการส่งคืนตัวเลือกคำขอข้อมูลรับรอง (PublicKeyCredentialRequestOptions) ไปยังไคลเอนต์ จากนั้นไคลเอนต์จะใช้ตัวเลือกเหล่านั้นด้วย navigator.credentials.get() เพื่อรับข้อมูลประจำตัวที่มีอยู่
// Simplified `/assertion/options` handler from authentication_handlers.go
func (s *server) handleAssertionOptions(w http.ResponseWriter, r *http.Request) {
// Get user from datastore by username.
u, _ := s.dataStore.getUser(r.Context(), optionsRequest.Username)
// Create PublicKeyCredentialRequestOptions using webauthn library.
requestOptions, _ := webauthn.NewAssertionOptions(s.webAuthnConfig, &webauthn.User{ID: u.UserID, Name: u.UserName, DisplayName: u.DisplayName, CredentialIDs: u.CredentialIDs})
// Save requestOptions and user info in session to verify credential later.
session.Values[WebAuthnRequestOptions] = requestOptions
session.Values[UserSession] = &userSession{User: u}
// Write requestOptions to response.
}
ตรวจสอบข้อมูลรับรอง:
เซิร์ฟเวอร์ตรวจสอบข้อมูลรับรองที่ได้รับผ่าน /asssertion/result
// Simplified `/assertion/result` handler from authentication_handlers.go
func (s *server) handleAssertionResult(w http.ResponseWriter, r *http.Request) {
// Get saved requestOptions and user info.
// Parse credential in request body.
credentialAssertion, _ := webauthn.ParseAssertion(r.Body)
// Get credential from datastore by received credential ID.
c, _ := s.dataStore.getCredential(r.Context(), uSession.User.UserID, credentialAssertion.RawID)
// Verify credential.
expected := &webauthn.AssertionExpectedData{
Origin: s.rpOrigin,
RPID: savedRequestOptions.RPID,
Challenge: base64.RawURLEncoding.EncodeToString(savedRequestOptions.Challenge),
UserVerification: savedRequestOptions.UserVerification,
UserID: uSession.User.UserID,
UserCredentialIDs: userCredentialIDs,
PrevCounter: c.Counter,
Credential: credKey,
}
err = webauthn.VerifyAssertion(credentialAssertion, expected)
// Update authenticator counter in datastore.
c.Counter = credentialAssertion.AuthnData.Counter
err = s.dataStore.updateCredential(r.Context(), c)
// Write "ok" response.
}
การแก้ไขความปลอดภัยมีให้สำหรับเวอร์ชันล่าสุดที่เผยแพร่
ในการรายงานช่องโหว่ด้านความปลอดภัยโปรดส่งอีเมล [email protected] และให้เวลาสำหรับปัญหาที่จะได้รับการแก้ไขก่อนที่จะรายงานต่อสาธารณะ
ลิขสิทธิ์ (c) 2019- ปัจจุบัน Faye Amacker
fxamacker/webauthn-demo ได้รับอนุญาตภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0 ดูใบอนุญาตสำหรับข้อความใบอนุญาตเต็มรูปแบบ