How Does MySQL Store and Verify Passwords in Its Database?
MySQL does not store passwords as readable text. It stores MySQL password hashes and verifies a login by hashing the password you type, then comparing that result to the stored value. If you want to see the shape of those hashes without poking a live server, give the MySQL password hash tool a spin.
What MySQL actually stores
At a basic level, a database account password is turned into a one-way digest and saved in the server’s authentication data. Older MySQL versions exposed account data in tables like mysql.user; newer versions still keep the credential material in server-managed privilege metadata, even if the exact storage details vary by version and plugin.
The key idea is simple: MySQL keeps something you can compare, not something you can reverse. That means the server can answer, “does this password match?” without ever needing to know the original plaintext again.
That one-way property is the difference between hashing and encryption. Encryption is designed to be undone with a key. Hashing is designed to be checked, not recovered.
How login verification works
When a client connects, MySQL does not take the submitted password and look it up as-is. It runs the candidate password through the account’s authentication scheme and compares the result to the stored hash or authentication response expected by that scheme. If the values match, access is granted.
In practice, the exact steps depend on the authentication plugin in use. Modern MySQL commonly uses stronger schemes than the old password formats people still remember from legacy systems, and the server may store plugin-specific metadata alongside the account record.
A simplified version looks like this:
client enters password: hunter2
server computes auth value: [derived hash]
stored auth value: [derived hash]
match? yes - allow loginThis is why plain text passwords in a database are such a bad idea. If an attacker gets the file or dump, the whole secret is immediately exposed. With hashes, they still need to crack or guess the password offline.
Hashing is not the same as encryption
People mix these up all the time, which is how password storage gets messy. A hash is a fixed-length fingerprint of the input. A cipher is a reversible transformation.
For passwords, reversibility is the problem. You do not want the database to contain a secret that can be unwrapped later, because that creates a second failure mode: stolen key, stolen passwords. Hashing avoids that by making recovery impractical, provided the algorithm and settings are decent.
That still does not make hashes magic armor. Weak passwords can still be guessed, especially if the attacker has the hash and can test candidates quickly. Slow hashing schemes and proper account policies matter because they raise the cost of each guess.
If you are comparing hash formats across systems, our guide on MariaDB password hashes is a useful companion. MariaDB and MySQL share some history, but the details do not always line up cleanly.
Common MySQL authentication formats
MySQL has used more than one password scheme over time. That matters because an old dump, a migration script, or a hand-edited account record may contain a format that looks unfamiliar at first glance.
- Legacy password hashes from older MySQL releases: short, weak by modern standards, and best treated as technical debt.
- Native password authentication: the classic MySQL plugin approach many people still encounter in older deployments.
- Modern authentication plugins: newer methods can use stronger derivation and handshake behavior depending on server version and configuration.
If you are auditing account data, the first thing to check is not the password itself. Check the account plugin and the stored string format. A mismatch there often explains why a login works in one environment and fails in another.
A quick sanity check can save time when you are staring at export files. If one environment shows a hash that starts with a plugin marker and another shows something shorter or older-looking, you are probably dealing with different authentication formats rather than a corrupted dump.
Why salts and slow hashing matter
Good password storage is not just “hash the string and move on.” Modern systems usually rely on salting and slower derivation so that two users with the same password do not end up with the same stored value, and so that brute-force guessing is expensive.
A salt is a unique random value mixed into the password before hashing. That defeats rainbow-table style precomputation and makes reused passwords less obvious in a leaked database. Slow hashing raises the cost per guess, which is exactly what you want when the attacker can test millions of guesses offline.
MySQL’s authentication layer has evolved over time to keep up with this reality. If you are still looking at ancient hashes, the right move is usually to rotate credentials, upgrade the auth method, and stop treating the old format as acceptable just because it still “works.”
What developers usually get wrong
The first mistake is assuming a password hash is safe just because it is not plaintext. Hashes can still be cracked if the password is weak. “Not reversible” is a property, not a guarantee of safety.
The second mistake is copying account data between systems without checking the authentication plugin. A dump can look fine and still fail at login because the target server expects a different plugin, version, or credential format.
The third mistake is eyeballing hash strings by feel. That is how people miss a bad export, a truncated column, or a copy-paste bug. When the string matters, compare the exact bytes, not the vibes.
If you need to compare two exports or see whether one record changed in a single character, the text diff tool is handy for spotting the difference fast.
See It in Action
Here is a practical example of how a password gets transformed into something MySQL can verify. The exact algorithm and output depend on the version and plugin, but the workflow is the same: input goes in, derived value comes out, and the database keeps the derived value.
plain password: correct horse battery staple
stored auth string: *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29
login attempt: correct horse battery staple
result: hash(login attempt) == stored auth string
status: acceptedNow change one character:
login attempt: correct horse battery stapLe
result: hash(login attempt) != stored auth string
status: rejectedThat tiny change produces a completely different digest. This is why hashes are useful for verification and terrible for “almost matching” comparisons. If you are auditing a migration and the stored value differs only in format, it usually means the account needs rehashing or reconfiguration, not just a quick patch.
In a real migration, you might see something like this in a dump or admin script:
-- old environment
'user_a'@'%' authentication_string = '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29'
-- new environment
'user_a'@'%' plugin = 'caching_sha2_password'
That is the point where people often realize the account data is not just “a password column.” It is a combination of stored credential material and server-side authentication rules.
Frequently Asked Questions
Does MySQL store passwords in plain text?
No. MySQL stores hashed or plugin-derived authentication data, not the readable password itself. A plain text password would be a direct breach waiting to happen. The whole point of the storage design is to make verification possible without exposing the secret.
Can you reverse a MySQL password hash back to the original password?
Not in the normal sense. Hashes are one-way, so you cannot decrypt them into the original password the way you would decrypt an encrypted file. You can only guess passwords and see whether a candidate produces the same stored value.
Why do some MySQL password strings look different from others?
Because different versions and authentication plugins use different formats. Older accounts may use legacy hashes, while newer accounts may use stronger plugin-based schemes. If the string shape changed after a migration, the account may be using a different auth method rather than a broken password.
How do I check whether two MySQL password hashes match?
Compare the exact stored values for the same account and plugin, or derive a candidate hash with the same algorithm and settings. Do not compare by length alone; different plugins can produce different-looking strings. If you are debugging exports, byte-for-byte diffing is safer than eyeballing.
The Bottom Line
MySQL stores passwords as hashes or plugin-managed authentication data so the server can verify a login without keeping the original password. That design is only as strong as the hash scheme, the password quality, and the account configuration around it.
If you are auditing a dump, migrating users, or just trying to understand what a stored credential string really means, check the plugin, compare the exact format, and treat legacy hashes as something to replace rather than preserve. When you want to inspect the output side of the process without touching a live instance, use the MySQL password hash tool and see the shape of the data for yourself.
Then, if you are dealing with mixed account data, compare the strings carefully, verify the auth plugin, and rotate anything that still looks like old baggage.