diff --git a/lib/anubis.go b/lib/anubis.go index af7238d8..3fd9e68f 100644 --- a/lib/anubis.go +++ b/lib/anubis.go @@ -454,6 +454,12 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { return } + if chall.Spent { + lg.Error("double spend prevented", "reason", "double_spend") + s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), "double_spend")) + return + } + impl, ok := challenge.Get(chall.Method) if !ok { lg.Error("check failed", "err", err) @@ -527,6 +533,12 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { s.SetCookie(w, CookieOpts{Path: cookiePath, Host: r.Host, Value: tokenString}) + chall.Spent = true + j := store.JSON[challenge.Challenge]{Underlying: s.store} + if err := j.Set(r.Context(), "challenge:"+chall.ID, *chall, 30*time.Minute); err != nil { + lg.Debug("can't update information about challenge", "err", err) + } + challengesValidated.WithLabelValues(rule.Challenge.Algorithm).Inc() lg.Debug("challenge passed, redirecting to app") http.Redirect(w, r, redir, http.StatusFound) diff --git a/lib/challenge/challenge.go b/lib/challenge/challenge.go index 1200e330..2553d0cd 100644 --- a/lib/challenge/challenge.go +++ b/lib/challenge/challenge.go @@ -9,4 +9,5 @@ type Challenge struct { RandomData string `json:"randomData"` // The random data the client processes IssuedAt time.Time `json:"issuedAt"` // When the challenge was issued Metadata map[string]string `json:"metadata"` // Challenge metadata such as IP address and user agent + Spent bool `json:"spent"` // Has the challenge already been solved? }