From 5d55fc18ee4ec977cb4351951b0a686a0c85ec82 Mon Sep 17 00:00:00 2001 From: Gani Georgiev Date: Sat, 25 Apr 2026 16:16:23 +0300 Subject: [PATCH] added dummy bcrypt check --- apis/middlewares_rate_limit_test.go | 4 ++-- apis/record_auth_with_password.go | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apis/middlewares_rate_limit_test.go b/apis/middlewares_rate_limit_test.go index de69644a..cf4f3d00 100644 --- a/apis/middlewares_rate_limit_test.go +++ b/apis/middlewares_rate_limit_test.go @@ -85,8 +85,8 @@ func TestDefaultRateLimitMiddleware(t *testing.T) { {"/norate", 0, false, 200}, {"/rate/a", 0, false, 200}, - {"/rate/a", 700, false, 200}, // (fixed window check) wait enough to ensure that it can't fit more than 2 requests in 1s - {"/rate/a", 800, false, 200}, + {"/rate/a", 800, false, 200}, // (fixed window check) wait enough to ensure that it can't fit more than 2 requests in 1s + {"/rate/a", 500, false, 200}, {"/rate/a", 800, false, 200}, {"/rate/a", 0, false, 200}, {"/rate/a", 0, false, 429}, diff --git a/apis/record_auth_with_password.go b/apis/record_auth_with_password.go index 7a52ff5c..2bd847bb 100644 --- a/apis/record_auth_with_password.go +++ b/apis/record_auth_with_password.go @@ -85,6 +85,11 @@ func recordAuthWithPassword(e *core.RequestEvent) error { return e.App.OnRecordAuthWithPasswordRequest().Trigger(event, func(e *core.RecordAuthWithPasswordRequestEvent) error { if e.Record == nil || !e.Record.ValidatePassword(e.Password) { + // dummy password check to minimize enumeration side-channel attacks + if e.Record == nil { + dummyPasswordCheck(e.App, e.Collection) + } + return e.BadRequestError("Failed to authenticate.", errors.New("invalid login credentials")) } @@ -115,6 +120,21 @@ func (form *authWithPasswordForm) validate(collection *core.Collection) error { ) } +// dummy password check to minimize side-channel attacks +// (performed with the collection configured field cost) +func dummyPasswordCheck(app core.App, collection *core.Collection) { + record := &core.Record{} + + // find any random existing record + err := app.RecordQuery(collection).Limit(1).One(record) + if err != nil { + return + } + + // the value and result doesn't matter, we just need a constant-time check + _ = record.ValidatePassword("") +} + func findRecordByIdentityField(app core.App, collection *core.Collection, field string, value any) (*core.Record, error) { if !slices.Contains(collection.PasswordAuth.IdentityFields, field) { return nil, errors.New("invalid identity field " + field)