Validating bcrypt password without selecting hash from database?

Good evening,

I’m writing the authentication layer of my api and ran into a problem with the password validation.

Currently the password hash is generated using bcrypt and stored in a Postgres database. When a user
sends a token-request, he also sends a plaintext password which needs to be validated against the hash in
the database.

Is there a way to check if it’s the right password without doing a SELECT query to the db and then comparing
plaintext and hash using the CompareHashAndPassword?

Not sure if I understand the problem. If the hash is in the database, you cannot avoid a SELECT until you also store the hash somewhere outside the database. Maybe some sort of in-memory caching layer might be what you are looking for? E.g.,

func getHash(username) {
    query cache for username
    if hash is in cache {
        return hash from cache
   }
   query db for username
   return hash from db result
}

Then, once the hash is cached, no more SELECT is required as long as the hash remains in the cache.

4 Likes

A example

func loginHandler(w http.ResponseWriter, r *http.Request) {
email := r.FormValue("Email")
pass := r.FormValue("Pass")

if email != "" && pass != "" {

	var u User

	query := aranGO.NewQuery("FOR u IN customer FILTER u.Email=='" + email + "' AND u.Active==1 AND u.Del==0 RETURN u")
	query.SetFullCount(true)
	cur, err := db.Execute(query)
	checkError(err)

	if len(cur.Result) == 1 {

		sendpassword := []byte(pass + salt)

		for cur.FetchOne(&u) {

			err = bcrypt.CompareHashAndPassword(u.Pass, sendpassword)
			if err != nil {
				
				return
			}

			setSession(u.Key, w, r)
			setFlash(w, r, "message", "OK")
			sendJsonMessage(w, r, "", "", "dashboard", false)
			return

		}
	} else {
		sendJsonMessage(w, r, "Invalid", "danger", "false", false)
	}
	return

} else {
	sendJsonMessage(w, r, "Check your inserts
}

}

As far as I could see (here and when I was searching the www), there is no way of comparing plaintext and hash without loading the hash into the app.

Isn’t it than better to just use SHA512 or a similar hashing method and use the username as a salt? This way I could easily
compare plaintext and hash without loading it into the app.

You csn"t compare to a hash you don’t have.

Checking a password is done by hashing it and then at hash with the hash that was created when the password was set. If the hashes are equivalent, the password is correct.

2 Likes

Okay, so if I understand you correctly, you want to be able to defer the actual comparison to the database — that’s what you’d “win” by having a known salt — you wouldn’t have to retrieve the hash to know the salt and have a string you can match against the db.

First things first — do not, under any circumstances, mess with the library provided hashing algorithms, or try to roll your own. The answers are contained in a cryptography seminar at your local mathematics degree and a current engagement with literature, and third party peer review of your solution, and I’m not really being flippant here — this is, honestly, what it takes. So please just don’t.

Now, the only solution to do what you want is to do the cryptography on the database side. This has some issues: it’s no longer really a Go question, you’re sending the plaintext over yet another (albeit short and presumably controlled) network connection, and you’re using unusual implementations (less eyes, less peer review, more risk).

Now, as far as I know (I haven’t used Postgres in ages), there is a Bcrypt implementation in Postgres. So, you can just use the same advice as for any other language: http://stackoverflow.com/questions/2647158/how-can-i-hash-passwords-in-postgresql. Do note the caveats in the top answer: note the mentions of Postgres slow query log, for example. Generally it’s best to hash the password at the earliest reasonable step and discard the plaintext — so please bite the bullet and do the select and then hash in the app. But yes, you can do that db-side, if you really have to.

First of all, thanks everyone for the answers. I should have been a bit more precise in my initial post, my excuses.

I couldn’t find an easy/safe way to verify if a user entered the right password without loading the hash into the application, so what I ended up doing is the following:

I switched from bcrypt to scrypt and now I generate the salt myself, hash the password and safe the hash in the password column and the salt in the salt column. As soon as a user enters a password, I get the salt from the database, rehash the password with the salt and do a select query with the hash.

I think this is the easiest/safest way to guarantee that a user entered the right password.

If someone knows a different way or sees a potential vulnerability with this method, please let me know.

I’d be careful about generating the salt yourself - you can easily end up with less randomness than you thought you had. It’s maybe not the hardest part of the process, but there’s a reason why implementations generally encapsulate the entire process.

I really wonder why you’re avoiding just selecting the hashed password. The chances of intercepting the hashed password in transit from database to application server are usually minuscule, and there are benefits to using reviewed, complete implementations.

  1. ComparePasswordAndHash or whatever func it is is designed to avoid timing attacks. I’m guessing you approach is not, and is therefore vulnerable to them.

  2. Doesn’t this approach lead to two DB queries instead of just one making it even worse?

Instead of simply retrieving a user, comparing hashes, and then moving on with your code (1 db query) you instead have to query for the salt, generate the hashed password,
then do a second DB query to verify the hashed password is valid.

The only way you could avoid this is if your salt wasn’t something you had to query for, in which case I would imagine you are making your salt less secure and more predictable for attackers. The purpose of a salt is to be an unpredictable random bit of data making things like rainbow table attacks impossible. If everyone used email addresses as the salt then a rainbow table for a specific user would be valid across many domains, making your site slightly less secure.

These two details may not seem like a huge deal, but when it comes to protecting your users I see no reason to cut corners. Especially to cut out a single DB query that happens what, once per day per user? Once a user is logged in you can use JWT or sessions so you are no longer going to gain anything from this approach.

All in all it sounds like you are trying to optimize something that likely isn’t a bottleneck in your application and your time would be better spent elsewhere.

Is there a clear performance reason that lead to you trying to optimize this? If so please share it so we can help you determine if there are other ways to speed things up without risking security.

The main reason I was initially looking for help in the first post was that I did not want to load the hash into my application and I was trying to find the most secure way to hash/salt and verify a user.

This was based on the fact that I learned a few years ago that I shouldn’t load the hash into the application because it could create security issues.

For me it does not really matter how many requests I do or which hashing I use as long as it’s secure.

Could you please explain what you meant with the timing attacks and ComparePasswordAndHash? I can’t quite follow you there.

I currently always do two requests, even if I could not find a user and a salt (I’m hashing a fake password with a fake salt) so that a potential attacker can’t see the difference between user found and no user found.

Modern practices around passwords are designed such that you could leak a password hash and it still should not easily leave a user super vulnerable. Yes, this is worse than never leaking the password hash, but it isn’t anywhere near the same as leaking an actual raw password. Attackers still need to create a rainbow table specific to that user’s hash to try to figure out their password, which is a lot of work.

The vastly simplified version of a timing attack is analyzing how long a web request (like a login attempt) takes in order to discern more information about the backend system (like what crypto functions are being used, the cost being used, etc) and then using that info for other attacks. IMO it is one of the smaller concerns when it comes to an auth system, but it is easy to prevent by using https://golang.org/pkg/crypto/subtle/#ConstantTimeCompare bcrypt’s ComparePasswordAndHash function does indeed use this behind the scenes, making all comparisons constant time.

It sounds like you are trying to prevent retrieving the hash from the DB so that it is never in memory and can’t be easily leaked, but truthfully I think you are better off following well known security practices because you are much more likely to introduce a security hole doing non-standard things (like you are trying to do). Just be sure to get rid of the hash as soon as you don’t need it.

If you really want to try to double down on making your retrieved hash more secure, check out this post - https://blogs.dropbox.com/tech/2016/09/how-dropbox-securely-stores-your-passwords/

To be clear, this article mentions using encryption, but it DOES NOT replace hashing the password and you should never replace password hashing with encryption. The two can be used together though to make a slightly more secure setup.

2 Likes

Thank you for the link/explanation. That answered every question I had.

EDIT: While writing the code I encountered some small thing. What would be the best way to get rid of the hash as soon as I don’t need it anymore?

Suppose the hash is saved under user.Password. Would user.Password = "" be enough or should I do something else?

And I could not find any information on the following: Does Go have the bcrypt limit of 72 chars?

You probably can’t get rid of it fully during the lifetime of the request. Whatever is calling your code, is probably going to store the message that triggered the authentication somewhere, or at least buffer some part of it. This means just eliminating the particular variable you had won’t achieve much, and reducing the lifetime of the plaintext under the lifetime of the request is going to be impractical. It’s best to just be done with your request ASAP (but not so fast that one can enumerate your users by timing requests), and be glad that buffer overflows are uncommon in Go code.

While this is mostly true I will mention that the one upside to zeroing out a password field is that you can’t accidentally log it anywhere. I wouldn’t get worried about people accessing random memory as much as I would worry about a random println outputting something sensitive to a log that stays on disk or gets sent to a third party logging service.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.