feathersjs-ecosystem / authentication-local

[MOVED] Local authentication plugin for feathers-authentication
https://github.com/feathersjs/feathers
MIT License
26 stars 15 forks source link

Handling concurrent hashing and comparing passwords #70

Closed ThePesta closed 6 years ago

ThePesta commented 6 years ago

The bcryptjs library used for creating hashes and comparing password async implementation is known to use the main thread. This causes the rest of a feathers application to be unable to perform other operations and handle new requests.

How do you handle multiple concurrent user sign ups and logins when the main thread is busy with the hash generating/password checking?

daffl commented 6 years ago

The linked issue also states that computation is split into chunks to make parralelization more efficient. So far I have not had or been made aware of anybody running into actual performance issues around password hashing.

daffl commented 6 years ago

Additionally this is also a security consideration:

For bcrypt to be effective, it needs to be THAT much slower, since it’s designed to raise the cost of password cracking. At 100ms, that means at least 10 passwords per second or faster for the attacker per machine; at that rate, a single machine w/o optimizations (GPU, etc.) can crack a 6-character non-complex password in an average time of 6 months. Realistically, at 100x speed, that means a 7-character password will fall in 45 days

You can also always customize this to your needs by implementing a custom verifier.

ThePesta commented 6 years ago

I've made a repo to test the response times of bcryptjs versus bcrypt

Two tests were ran: 1) Max concurrent user creation requests before the default timeout of 5 seconds was reached 2) Sustained number of user creation requests over 30 seconds

Concurrent requests results

- Bcryptjs Bcrypt (1 core) Bcrypt (2 core) Bcrypt (4 core)
Max signups 7 10 20 35
Avg time (ms) 4527 3967 2934 2737
Min time (ms) 4524 1971 951 658
Max time (ms) 4531 4783 4834 4878

The simultaneous test scenario where # requests are fired simultaneously show that the maximum number of simultaneous signups with bcryptjs is 7 on a local machine with a best case scenario not accounting for network latency or additional code execution time from other hooks/operations on the server. With the chunking technique used in bcryptjs each request has to wait for the other requests.

Utilizing the bcrypt running on just one core we increase this to 10, as well as not blocking the first requests to receive their response as indicated by the min times. Multi core scaling is an easy benefit as well.


Sustained requests results

Sustained # of requests per second over 30 seconds was also measured (with the default timeout of 5 seconds):

Bcryptjs

With bcryptjs, only one request per second can be sustained infinitely, with two already having issues five seconds in.

Bcrypt (1 core)

With bcrypt running on one core a max of two requests per second can be sustained infinitely, with a less sharp increase in response times when running three or more requests per second.

Bcrypt (2 cores)

With bcrypt on two cores, a max of four requests per second can be sustained infinitely.

Bcrypt (4 cores)

With bcryptjs on four cores, a max of seven requests per second can be sustained infinitely

Running bcryptjs versus bcrypt with one core already shows a doubling in performance, with large gains in going to two cores


Are these performance numbers with bcryptjs acceptable? As these results only show local response times before network latency is added. If they aren't I'd like to propose a switch to bcrypt, however I am unsure if the dependencies on C/C++ of bcrypt has an impact to existing projects.

daffl commented 6 years ago

Thank you for the detailed benchmark. Local authentication used to use bcrypt but it was causing many compilation issues on different systems (especially Windows of course) so it was changed to bcryptjs and making things flexible enough to change the password hashing to whatever you need.