“They’re encrypted in transit, so it’s fine.”
That was the developer’s response when our tester reported that their staging environment was exposing every user password in plain text during a recent penetration test.
Our client wanted to check for vulnerabilities in their web application. A file upload flaw and a SQL injection vulnerability gave us direct access to the database. That database, assumed to be safe for testing, turned out to be a full copy of production data with the passwords stored in plain text.
When we alerted the customer, the developer pushed back. The passwords were obfuscated and encrypted in transit, so they believed the risk was minimal. But the problem wasn’t in transit. The problem was that the vulnerabilities gave us access to the database itself. No need to intercept anything. We could already see every password as if we were attackers.
Eventually, the developer conceded. But this kind of mistake is more common than it should be.
Since January 2025, our penetration testing team has uncovered this issue in at least one engagement per month with organisations developing their own in-house software and storing passwords in plain text.
In a real-world breach, any successful exploit against your application is serious. But the consequences of neglectful password handling can make it far worse. Now you’re not just dealing with a compromise of your system, you’re dealing with the potential for follow-on breaches against your customers, especially those who reuse passwords across services. It snowballs and your reputation takes the hit.
What’s So Bad About Plain Text Password Storage?
Storing passwords in plain text means saving them exactly as users enter them, without any form of encryption or protection. If someone gains access to your database, they can read every password immediately, just like opening a document with no password or restriction.
This is a serious risk. Databases can be exposed in many ways: through software vulnerabilities, misconfigured servers, insider threats, or even something as simple as a forgotten backup left unsecured. Once exposed, plain text passwords give attackers instant access to user accounts, and potentially to other systems if users reuse passwords.
Even if your infrastructure seems secure, accidents happen. Developers might copy production data into a test environment without proper safeguards. A backup might be stored in a location that lacks access controls. All it takes is one oversight for a breach to occur.
Plain text storage removes any margin for error. It turns a security incident into a full-blown compromise.
A Breach is Bad but Plain Text Makes it Much Worse
When passwords are stored in plain text, the fallout from a breach escalates instantly:
- Everyone’s password is exposed. There’s no hashing, no salt, no protection. Attackers know the exact passwords.
- People reuse passwords. That leaked password could unlock their work email, their online banking, their cloud storage.
- You’ll be blamed. And rightly so. Even your users will ask: “Wait, they didn’t hash our passwords?!”
The damage to trust can be significant. And for a smaller company, one breach might be the beginning of the end.
How Should You Be Storing Passwords in Your Application?
If you’re building software that handles login credentials, here’s how you should be doing it:
1. Hash the Password
Instead of saving the actual password, store a hash, a one-way transformation of the password into a fixed string. This makes it impossible to reverse-engineer the original password from the stored value. When a user logs in, the system hashes the password they enter and compares it to the stored hash.
2. Add a Salt
A salt is a unique, random string added to each password before hashing. This ensures that even if two users have the same password, their hashes will be different. Salting protects against precomputed attacks like rainbow tables.
3. Use a Pepper
A pepper is a secret value added to the password (along with the salt) before hashing. Unlike a salt, the pepper is the same for all users and is stored separately from the database, typically in the application’s environment or configuration. This adds another layer of protection in case the database is compromised.
4. Apply Key Stretching
Key stretching makes password hashing more secure by intentionally slowing down the hashing process. This is done by running the hash function many times or using algorithms that are computationally expensive. The goal is to make brute-force attacks impractical.
Use password-specific hashing algorithms that support key stretching, such as:
- bcrypt
- scrypt
- Argon2
These algorithms are designed to be slow and resistant to attacks using modern hardware like GPUs.
What Does A Hashed Password Look Like When Stored?
When passwords are stored securely, they don’t look anything like the original text users type in. Instead, they’re transformed into long, unintelligible strings using a process called hashing, combined with salting.
Here’s a simplified example:
User1
EvolveN0rth!23
dl4dkp
jYvJ5a1LU28Mcvq12Z6zxNUd4pYBjU==
User2
ClumpDuckF(am!ily
94idss
VozuMRbVaADK69cVB6Fu56tNM95uOlMa
User3
EvolveN0rth!23
z9x8y7
xCTVgkMLj1dEiSd48L8yAiXvCZpnxfFK
In this example, even though User1 and User3 chose the same password, the system adds a unique salt to each one before hashing. This ensures that the final stored value is different for each user. In a real database, you’d see only the salted hash (and possibly the salt stored alongside it), not the original password.
This approach protects against several types of attacks:
- Rainbow table attacks: Where a precomputed list of password hashes is used to quickly reverse-engineer plain text passwords. Salting passwords makes this attack ineffective because it ensures each hash is unique, even for identical passwords.
- Credential reuse detection: Attackers can’t tell if two users have the same password.
- Brute-force resistance: When combined with key stretching, hashing becomes computationally expensive, therefore much slower, to try every combination.
In short, hashed and salted passwords look like random gibberish in the database and that’s exactly what you want.
A Bit of Tough Love
Building your own authentication system? We recommend reconsidering and looking at alternatives such as authentication frameworks or identity services.
Writing your own authentication mechanism for an in-house application is generally a bad idea because authentication is one of the most sensitive and complex parts of any system. Even seemingly small mistakes, like improper password storage, insecure session handling, or weak token generation, can lead to serious vulnerabilities.
Established authentication frameworks are built by security experts, tested by large communities, and regularly updated to address evolving threats. Recreating that level of security and reliability in-house is extremely difficult and time-consuming.
Moreover, modern identity services go beyond just verifying usernames and passwords. They offer features like single sign-on (SSO), multi-factor authentication (MFA), social login integration, and centralized user management. These services are designed to scale, comply with security standards, and integrate easily with other systems. Building all of this from scratch not only increases development effort but also introduces long-term maintenance burdens.
By using a trusted authentication framework or identity service, you reduce risk, save development time, and ensure your application meets modern security expectations. In short, reinventing the wheel in such a critical area is rarely worth the cost or the risk.
Further Reading and Resources
If you’re looking to deepen your understanding of secure password storage and authentication best practices, here are some trusted resources to explore:
- OWASP Password Storage Cheat Sheet – A comprehensive guide from the Open Web Application Security Project on how to securely store passwords using modern cryptographic techniques.
- OWASP Authentication Cheat Sheet – Covers broader authentication practices, including session management and multi-factor authentication.
By following these resources, you’ll be better equipped to build secure systems and avoid the costly pitfalls of poor password handling.
Need support with password storage or broader application security?
Our team of penetration testers and security consultants can help you identify issues before they become incidents. Call us on 01748 905 002 or email info@evolvenorth.com to get started.
