Просмотр исходного кода

fix(webauthn): check ownership in saveCredential on WebAuthn rename

While both deleteCredential either validate or pass down a uid,
saveCredential doesn't. This isn't exploitable as an authenticated attacker
would need to guess the 32 bytes handle of another one, but it doesn't hurt to
explicitly check if a user is only operating on their own user.
jvoisin 1 неделя назад
Родитель
Сommit
10bdbf82b9
2 измененных файлов с 16 добавлено и 7 удалено
  1. 9 5
      internal/storage/webauthn.go
  2. 7 2
      internal/ui/webauthn.go

+ 9 - 5
internal/storage/webauthn.go

@@ -147,13 +147,17 @@ func (s *Storage) WebAuthnSaveLogin(handle []byte) error {
 	return nil
 }
 
-func (s *Storage) WebAuthnUpdateName(handle []byte, name string) error {
-	query := "UPDATE webauthn_credentials SET name=$1 WHERE handle=$2"
-	_, err := s.db.Exec(query, name, handle)
+func (s *Storage) WebAuthnUpdateName(userID int64, handle []byte, name string) (int64, error) {
+	query := "UPDATE webauthn_credentials SET name=$1 WHERE handle=$2 AND user_id=$3"
+	result, err := s.db.Exec(query, name, handle, userID)
 	if err != nil {
-		return fmt.Errorf(`store: unable to update name for webauthn credential: %v`, err)
+		return 0, fmt.Errorf(`store: unable to update name for webauthn credential: %v`, err)
 	}
-	return nil
+	rows, err := result.RowsAffected()
+	if err != nil {
+		return 0, fmt.Errorf(`store: unable to update name for webauthn credential: %v`, err)
+	}
+	return rows, nil
 }
 
 func (s *Storage) CountWebAuthnCredentialsByUserID(userID int64) int {

+ 7 - 2
internal/ui/webauthn.go

@@ -370,7 +370,8 @@ func (h *handler) renameCredential(w http.ResponseWriter, r *http.Request) {
 }
 
 func (h *handler) saveCredential(w http.ResponseWriter, r *http.Request) {
-	_, err := h.store.UserByID(request.UserID(r))
+	userID := request.UserID(r)
+	_, err := h.store.UserByID(userID)
 	if err != nil {
 		response.HTMLServerError(w, r, err)
 		return
@@ -384,11 +385,15 @@ func (h *handler) saveCredential(w http.ResponseWriter, r *http.Request) {
 	}
 
 	newName := r.FormValue("name")
-	err = h.store.WebAuthnUpdateName(credentialHandle, newName)
+	changed, err := h.store.WebAuthnUpdateName(userID, credentialHandle, newName)
 	if err != nil {
 		response.HTMLServerError(w, r, err)
 		return
 	}
+	if changed == 0 {
+		response.HTMLNotFound(w, r)
+		return
+	}
 
 	response.HTMLRedirect(w, r, h.routePath("/settings"))
 }