PostgreSQL Password Hash Generate MD5 and SCRAM-SHA-256 Hashes

PostgreSQL password hash — Chunky Munster

A PostgreSQL password hash is the stored verifier PostgreSQL uses to check a login without keeping the plaintext password. If you need to inspect, compare, or generate the common formats, give our free PostgreSQL Password Hash tool a spin.

What PostgreSQL actually stores

PostgreSQL does not normally save the user’s real password. It saves a derived value that can be checked during authentication, which is why the server can confirm a login attempt without ever learning the original secret again.

The exact shape of that stored value depends on the authentication method and the server version. In older clusters you’ll often see MD5-based entries. In modern deployments, SCRAM-SHA-256 is the standard choice and the one you want for new systems.

That matters in day-to-day work. If you’re debugging a failed login, checking a dump, or migrating an old instance, the password hash format tells you what era you’re dealing with and how much security debt is sitting in the room.

For a broader primer on password hashing schemes, see our guide to how bcrypt password hashing works. It’s not PostgreSQL-specific, but the same basic idea applies: store a verifier, not the secret.

MD5 in PostgreSQL: legacy, simple, and dated

PostgreSQL’s MD5 format is the older one most people run into when auditing an ancient database. It usually looks like md5 followed by a 32-character hex string.

A typical example is:

md5f4d1c8b3b1c6f8f6d4f2f1d5e3a9b1c7

Under the hood, PostgreSQL’s MD5 password storage is not just a raw MD5 of the password. The server combines the password and username into a derived value, then stores that result. That makes it slightly less trivial than plain MD5, but it is still a legacy scheme and not the direction you want for a new deployment.

Two practical warnings:

SCRAM-SHA-256 is the modern default

SCRAM-SHA-256 is the modern PostgreSQL password authentication scheme. It stores a salted verifier plus metadata that allows the server to validate a login without keeping the password itself. Compared with MD5, it is much better aligned with current password-handling practice.

A SCRAM verifier looks more verbose because it carries more parts. A simplified example looks like this:

SCRAM-SHA-256$4096:QSXCR+Q6sek8bf92$iYh7a4gJ9Z3J0JYw1fVYqA==$7mQ1Zr0fG4G2gX1nq1K3R3x5H8tK9c8y0tqz8nKxZ7M=

Do not read too much into the exact bytes. The useful part is the structure: algorithm name, iteration count, salt, and stored verifier data. That structure is why SCRAM is more resistant to offline guessing than a plain legacy hash format.

In real ops work, SCRAM is what you want to see after a password rotation or during a fresh install. If your server supports it, and especially if your clients do too, use it.

How to tell what you are looking at

Most of the time you only need a quick visual check. The prefix gives the game away.

If you are looking inside PostgreSQL itself, the current password verifier for a role is usually managed through role commands rather than direct table editing. In older versions, admins sometimes had to inspect system catalogs more often; on modern systems, you generally work through ALTER ROLE and authentication settings instead.

When a login fails, the hash format is only one clue. You should also check the authentication method in pg_hba.conf, whether the client library supports SCRAM, and whether the role was rotated recently. A valid verifier can still fail if the authentication chain is misconfigured.

When migrations get messy

The most common real-world mess is a mixed environment. You upgrade PostgreSQL, but an old app, driver, or connection pool still expects an authentication flow from the previous era. The database may be ready for SCRAM, while the client stack is still speaking MD5-shaped habits.

That shows up as one of those unpleasant “works in staging, fails in production” problems. The password itself may be fine. The verifier may be fine. The mismatch is usually in client support, server auth rules, or both.

Good migration hygiene is boring but effective:

  1. Inventory which roles still use legacy password storage.
  2. Check every application driver for SCRAM support.
  3. Update pg_hba.conf before forcing auth changes.
  4. Rotate credentials in a controlled window, not mid-incident.

If you also work with other SQL engines, the shape of stored passwords differs there too. Our guide to MariaDB password hashes is a good comparison point if you’re untangling multiple database backends.

Security basics without the mythology

A password hash is not meant to be reversible. If someone can “decode” it back into the original password, something has gone badly wrong. The verifier should only let the server answer one question: does this login attempt match the stored secret?

That also means you should not treat a hash dump as harmless just because it is not plaintext. Hashes can still be attacked offline if an attacker gets a copy. The stronger the scheme, the slower and more expensive that attack becomes.

For PostgreSQL, the practical rule is straightforward: prefer SCRAM-SHA-256, rotate away from MD5 where you can, and keep your client drivers current. The crypto lecture matters less than the operational habit.

A Worked Example

Say you inherit an old database and want to see whether a role is still using MD5. You find a stored password-like value and it begins with md5. That tells you the cluster is carrying a legacy verifier, which is a good cue to plan a reset.

Here is a simplified before/after view of what you might see during a migration:

Before:
md5f4d1c8b3b1c6f8f6d4f2f1d5e3a9b1c7

After:
SCRAM-SHA-256$4096:QSXCR+Q6sek8bf92$iYh7a4gJ9Z3J0JYw1fVYqA==$7mQ1Zr0fG4G2gX1nq1K3R3x5H8tK9c8y0tqz8nKxZ7M=

In a real migration, you would not hand-edit those values. You would update the role password through PostgreSQL, confirm the client stack supports SCRAM, and test the connection from the app before cutting over traffic.

If you are generating sample values for documentation, onboarding, or a lab note, the main thing to check is the format, not the exact bytes. A tool like the PostgreSQL Password Hash generator is useful for seeing the difference between the legacy and modern shapes without guessing.

Frequently Asked Questions

How does PostgreSQL store passwords?

PostgreSQL stores a verifier, not the plaintext password. Depending on the authentication method and server version, that verifier may be MD5-based or SCRAM-SHA-256. The database uses the stored value to validate logins without needing the original password.

What does an MD5 password hash look like in PostgreSQL?

It usually starts with md5 followed by a 32-character hexadecimal string. That format is the old legacy style and is still seen in older clusters. If you find it in a modern environment, it is usually a sign to plan a migration.

Why is SCRAM-SHA-256 better than MD5 for PostgreSQL?

SCRAM-SHA-256 includes a salted verifier and iteration-based protection, which makes offline guessing more expensive. MD5 is a legacy scheme and is much weaker by current standards. For new systems, SCRAM-SHA-256 is the safer default.

Can I convert a PostgreSQL MD5 hash back to the original password?

No, not in any normal or legitimate way. A password hash is designed to be one-way. If you need access, the right move is to reset the password rather than trying to reverse the stored verifier.

Wrapping Up

The useful thing to remember is simple: a PostgreSQL password hash is a verifier, not a password vault. MD5 is the old format, SCRAM-SHA-256 is the modern one, and the prefix usually tells you which world you’re looking at.

If you are auditing a cluster, debugging authentication, or preparing a migration, check the hash format, confirm the server auth settings, and make sure your client libraries can speak the same dialect. Then verify the result with a test login before you declare victory.

If you want to inspect or generate sample values in your browser, use the PostgreSQL Password Hash tool and compare the output side by side. It is faster than staring at a mystery string and hoping it explains itself.

// try the tool
PostgreSQL Password Hash tool a spin →
// related reading
← all posts