Navidrome: Authentication improvements

Created on 28 Apr 2020  路  13Comments  路  Source: deluan/navidrome

Brute-force protection

Currently the authentication is brute-forceble as in, no limits on the the amount of attempts or the period wherein it's taken place. Meaning an attacker could try guessing as long and as fast as he wants without any interruption.

I suggest to allow two or three wrong attempts normally. Then doing two things:

  1. blocking login for that IP address with delay increases (exponentially) for every wrong attempt following (but an attacker can use different IP addresses)
  2. blocking the user login with delay increases (exponentially) for every wrong attempt (ignoring the attackers IP, useful when an attacker only attacks one username specifically and IP address block won't work).

Also to make brute-forcing harder you could consider something like reCAPTCHA.

Session binding / Session expiration

The session should be bind to the IP-address and/or user-agent. If any of those changes during the session, we can destroy the session as that might be a sign of session hijacking. That is especially easy if people don't use SSL. Binding it to the IP-address and user-agent solves that. Also it would be great to be able to configure the session timeout time on the settings page.

Password Hashing / Policy

Clear-text password storage is not done. https://github.com/deluan/navidrome/issues/202. Password should always be hashed preferably salted also, never encrypted. We should make an API token or insecure and very restricted (read-only) credential if that's needed for compatibility with other tools.

I was just allowed to make an admin user with a one-letter password. I would at activate some minimal password policy. Minimal 6 characters, one char of [a-z][A-Z][0-9] and a special char. Not allowing the password to be identical (or very similar) to the username or e-mail address. _Also, probably one, two or three letter usernames should be prevented. I am not sure if there is a maximum length on the password now, but there should not be or it should be as high as possible._

Two-factor authentication

To greatly improve the authentication we could think of introduction of the open standard HOTP/TOTP two-factor authentication (which I would also like to see a lot). It works with all current TFA apps like Google Authenticator, Microsoft Authenticator, Authy.

Other

Lastly, I just removed myself (test user), that should not be possible. Also, when I did that I wasn't logged out or sent back to the login page. So I stayed without valid session in the application showing me only "internal server errors" (luckily). Also I noticed that email is a required field when adding a new user but not when editing it. I should make it consistent.

And of course all of above suggestions could be optional and configurable in the settings page. But personally I would always opt for a secure-by-default and privacy-by-default option for as much as possible without degrading the user experience or limiting functionality for advanced users too much.

enhancement security

Most helpful comment

Captchas are a disaster in term of accessibility: people struggling to type their password would have their live made more difficult if they have to fill captchas. Moreover, blocking user login will lead to denial of services: an attacker can trivially throw a ton of login attempts, preventing legitimate users from using the service.

Session-binding is a bad idea in 2020, since everybody is roaming a lot.

As for password policy, something like zxcvbn would be better than enforcing some arbitrary rules.

I'm not sure I understand the value of OTP here: an attacker could perform authentication bruteforce via the API, and gain almost the same privileges.

All 13 comments

Captchas are a disaster in term of accessibility: people struggling to type their password would have their live made more difficult if they have to fill captchas. Moreover, blocking user login will lead to denial of services: an attacker can trivially throw a ton of login attempts, preventing legitimate users from using the service.

Session-binding is a bad idea in 2020, since everybody is roaming a lot.

As for password policy, something like zxcvbn would be better than enforcing some arbitrary rules.

I'm not sure I understand the value of OTP here: an attacker could perform authentication bruteforce via the API, and gain almost the same privileges.

  • I agree on Captcha's, just throwing in some ideas. I'm not a fan of it also. It's annoying but effective (slowing down) when implemented well. Maybe make it available optional and of course keep it disabled by default.
  • Regarding DoS that is a whole different issue. It has not much to do with blocking the user. DoS still prevents the attacker from getting access.
  • Session-binding is only bad when roaming, indeed. But then again, since the web player doesn't work on a mobile device (playing in background), roaming is unlikely. I would still suggest to do this, or at least make the option. Disabled/enabled by default and leave it open for user choice.
  • Agreed, zxcvbn or password strength measurement is definitely better but compared to the situation now, anything (including a simple policy) is better than nothing. But, I would also prefer your solution.
  • OTP value, at this point that is true that's why the API should have very restricted access to do only what is really needed, preferably in a read-only mode and with credentials different than those used for authentication to the Web UI. But that's also another discussion. But I'm not too much familiar with the API situation at this moment.

Would it be possible to just restrict navidrome to 1 auth attempt per 10 seconds? That would slow down any bruteforcing attempt considerably.

I'm gonna work on these improvements next. My considerations and what I'm planning to do:

  • Brute-force: I'm gonna add a simple rate limiter to the authentication endpoints, with a configurable attempts/minute rate (default 30/minute?)
  • Session binding: Agree with @jvoisin, don't think it is a good idea, and we do have mobile users, Navidrome works well in the mobile browser (though I'd not recommend it :P) That being said, I may bind the session to the user-agent, after making sure there's no drawbacks
  • Session timeout: The configuration was added a while ago
  • Password hashing: This will be tracked in #202
  • Password Policy: I will add zxcvbn integration, with configurable minimum score (default 2/4)
  • Removing own user: This is a bug and will be fixed

  • Blocking/Delayed logins, Captcha, Two-factor authentication: These are out-of-scope for Navidrome. Remember: Navidrome is not a service, it is a server! When installing your own server you should use a reverse proxy to harden your installation, and these features are better suited to be implemented/configured/supported by this inbound proxy server. I may even implement a way to disable authentication all-together, allowing the (power) user to use Navidrome with something like Vouch to provide SSO/OAuth2 authentication

  • About the rate limiting, you might want to use exponential backoff instead of a linear delay, and be mindful about ipv6 addresses :)
  • Session binding to user-agent doesn't improve anything security-wise, and will disconnect users when they update their browser.

I'll search for a ready to use implementation of exponential backoff, but I don't want to spend too much time on this, as rate-limiting also falls into the category of things that are responsibility of the inbound proxy server, IMHO.

For session bind, I think having to login when the browser is updated is a small price to pay for a more secure experience, don't you think?

What security property does it bring? If an attacker is able to intercept a session cookies, they'll likely able to intercept the user-agent as well :P

Yep, you are right :P

Rate limit using a Sliding Window strategy was implemented in f0160f5d2adc77291afd56329f17377daa802bdc, will be part of the v0.26.0 release

Is this by IP address? With IPv6 you probably want to rate-limit it by IPv6 prefix - i.e. limiting logins from the same /56, /60 or /64, since it's trivial to generate a new IPv6 address for every login attempt.

It is based on IP address. It was implemented using this middleware: https://github.com/go-chi/httprate

The algorithm used to create the key for the limiter is here: https://github.com/go-chi/httprate/blob/master/httprate.go#L24. When I'm back at this, I may take a look on ways to improve it

@jvoisin Is there anything newer than zxcvbn that you recommend? Last release of zxcvbn was 4 years ago, has some issues with modern JS and lots of open PRs... :(

I don't know any alternatives :/

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wanieru picture wanieru  路  3Comments

brianpierson2020 picture brianpierson2020  路  7Comments

BobWs picture BobWs  路  3Comments

ElleshaHackett picture ElleshaHackett  路  6Comments

yarg-kane picture yarg-kane  路  5Comments