fusion's current password mechanism is vulnerable to a timing attack:
https://en.wikipedia.org/wiki/Timing_attack
Because fusion checks passwords using simple character-by-character string comparison, a password attempt that begins with the correct characters will take longer to evaluate than one that starts with incorrect characters. For example, if the correct password is 'platypus123' then a password attempt of 'plates' will take longer to evaluate than 'spoons' because 'plates' and 'platypus' share a common prefix. An attacker who attempts the password 'plates' will know that they likely have the correct prefix.
To prevent the timing attack, this change hashes the user's password using PBKDF2 and compares hashes using subtle.ConstantTimeCompare, which is specifically designed to prevent timing attacks.
It feels a bit messy that the entire program has write access to the configuration as a shared global object. Shared globals make it more difficult to reason about a program's behavior.
This rewrite reduces the problem a bit by making the shared global state read-only after the client calls conf.Load.
It caught me by surprise during development that fusion was reading environment variables from .env, as that's not really clear anywhere.
I think it's a bit of a gotcha, so I added logging to make the behavior clearer, and I renamed the default .env file to .env.example so that the user has to explicitly choose to use a .env file.
Keeping the real .env file out of source control also protects developers from accidentally committing their real password to source control and leaking it by mistake.