Skip to content

improve basic auth performance by caching expensive password hashing computations  #7897

@wbolster

Description

@wbolster

tl;dr:

using http basic auth with high bcrypt complexity results in high cpu load and slow response times; caching may alleviate this.

Do you want to request a feature or report a bug?

a feature (or a performance bug, depending on how you look at it)

What were you trying to do?

i want to use basic auth (over tls) with strong bcrypt complexity as a simple but sensible security setup for things like the traefik dashboard.

What did you expect to see?

i expected it to work, and it does…

What did you actually see?

…but it slows down every request. merely having the dashboard open in a browser results in periodic polls to the /api endpoints, and each request is slow and eats CPU resources at the server.

What do you think can be done about it?

this slowness seems unnecessary. similar things have been reported before (e.g. #4791) but it seems it did not get the proper explanation/attention.

each request will have a valid user/pass in normal situations, since that is how basic auth operates. in practice, the basic auth middleware invokes goauth.CheckSecret for every request (source code).

the main problem here is that the code performs a (potentially very slow) bcrypt operation too often: it is called for every request by the same user with the exact same inputs, giving the exact same results (bcrypt is a pure operation). this is simply wasteful, resulting in slow responses and high cpu usage.

my suggestion would be to use a small in-memory (!) caching mechanism to track recent valid authentication attempts. for the expected use cases such as traefik dashboard + basic auth + strong bcrypt + small amount of users, this will likely avoid 99%+ of all slow operations.

the cache would contain a mapping from a key (user, pass, realm) to any value. presence of a key in the cache means it was validated correctly, absence means a real bcrypt check has to be done (which would result in .Add() when successful). the thread-safe Cache implementation in the golang-lru package (source, docs) may be helpful here.

note that there seem no additional security implications here: secrets are only ever kept in memory, and they are already in memory to begin with. but in practice it may be advisable to use a cheap(er) hash algorithm (e.g. sha512) to hash the user/pass/realm combination, and use that as the map key for the cache.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions