Is Postgres password-based authentication secure?

If using SSL, then what PostgreSQL does is fine. If not using SSL, but still doing the authentication across the network, then what PostgreSQL does stinks. Their games with MD5 are worthless, but not because they use MD5. MD5 has its own issues, but there they are just misusing it awfully.

With "cleartext password" authentication, the client shows a user name and a password, and the server accepts them if they match what the server stored.

With "md5" authentication, the client shows a value (which happens to be the MD5 hash of the concatenation of the password and the user name) and the server accepts it if it matches what the server stored.

So you see it: in both cases, the client shows a bunch of bytes to the server, always the same sequence. It suffices for an attacker to tap on the network to observe these bytes, and then connect to the server and sends the same bytes to be granted entry. That the bytes are the result of a MD5 hash is completely irrelevant here. This MD5 hash is said to be password equivalent. As long as the connection can be eavesdropped at all (i.e. no SSL), then security goes down the drain.

See this page for the details of the MD5 computation. They call it "encryption", which it is not at all (hashing is not encryption).

We could even argue that using this MD5 decreases security: when a plaintext password is used, it can be forwarded to another authentication server (using Kerberos, LDAP,...) and that authentication server could then employ strong storage techniques (see password hashing). With the PostgreSQL-specific MD5 hash, this cannot apply. When the "md5" authentication method is used, it MUST be against a table in the database which contains all these "md5" value as is. An attacker who can get his hands on, say, a backup tape or an old disk, will immediately obtain many free accounts on the database.

And, I insist, nothing in all this has anything to do with MD5 cryptographic weaknesses. All of this would still apply if MD5 was replaced with SHA-512.


Yes, using MD5 is safe. When a client wants to authenticate, the server sends a random salt value. The client uses that value, along with the password to generate a MD5 hash. Because a random salt is used, an attacker can not use a dictionary attack. Also, because the salt is changed each time a client authenticates, this is no ability to replay old authentication requests.

Details here in the AuthenticationMD5Password section.


TLDR. It can be secure if you configure it properly and use good long passwords.

1. In postgres the md5 auth-method means client-side hashing (discussions: 1, 2), which makes hashes password equivalent. A hash in the db is compared to a hash received from a client. An attacker can use any hash from stolen pg_shadow table directly, without even spending time to crack the md5.

It's much safer to assume the hashes will be stolen eventually and to avoid client-side hashing.


You can actually just take a look at the code, it's quite simple: https://github.com/postgres/postgres/blob/master/src/backend/libpq/crypt.c#L141 <— direct link to this line:

 if (strcmp(crypt_client_pass, crypt_pwd) == 0)

See what happens when the port->hba->auth_method == uaMD5. Yes, one can't intercept a hash clear-text, it's again salted and hashed. But when stolen by any other attack, it can be used directly without cracking.


2. Unsurprisingly, you can use server-side md5 hashing with postgres by using the password auth-method and create user whatever with encrypted password.

These methods operate similarly except for the way that the password is sent across the connection, namely MD5-hashed and clear-text respectively.

Use SSL to protect the clear-text password. You probably should know the way its hash is stored — the salt is reused:

 /* Encrypt user-supplied password to match stored MD5 */
 if (!pg_md5_encrypt(client_pass,          // const char *passwd
                     port->user_name,      // const char *salt
                     strlen(port->user_name),
                     crypt_client_pass))   // char *buf

One could even create random generated throwaway usernames to use them as salt, but I'm not sure if it's a good idea, this is error prone.

While the general recommendation is to migrate from md5, it's still not broken for password hashing. Relevant question: Why do people still use/recommend MD5 if it is cracked since 1996?

Don't use short passwords. Long, high-quality random passwords are still safe.

For a quick estimation, these (unfortunately, quite old) links have some numbers:
http://blog.codinghorror.com/speed-hashing/
https://security.stackexchange.com/a/33684/41687

Update: thanks to RhodiumToad from #postgresql channel on irc.freenode.net for clarifying that md5 is still not broken for password hashing, good long passwords will save the day.