What Happens When You Store Passwords in Text Files

Storing passwords in text files (.txt, .json, .csv) is one of the most dangerous patterns in vibe-coded applications. Attackers exploit directory traversal and server misconfiguration to download these files directly, gaining every username and password in cleartext. Sherlock Forensics audits password storage and authentication systems in AI-built apps starting at $1,500.

The Flat File Database Problem

You asked your AI coding tool to build a user registration system. It needed somewhere to store the data. Instead of setting up PostgreSQL, MySQL or even SQLite, the AI created a simple file. Maybe it is called users.txt. Maybe data/accounts.json. Maybe db/users.csv. The file contains usernames and passwords. It works. Users can register and log in.

What you do not realize is that you have just created the easiest possible target for any attacker. This is not a hypothetical risk. At Sherlock Forensics, we find flat file password storage in roughly one out of every four vibe-coded applications we audit. Here is exactly what happens when an attacker finds yours.

The Attack Path: Step by Step

Step 1: Discovery

The attacker visits your site. They do not start by trying to hack your login form. They start by looking for files. They try common paths that AI tools use when generating file-based storage:

yoursite.com/data/users.txt
yoursite.com/data/users.json
yoursite.com/db/accounts.json
yoursite.com/users.csv
yoursite.com/database/users.txt
yoursite.com/storage/credentials.json

This takes seconds. Automated tools try hundreds of common paths per minute. If your server returns a 200 response instead of a 404, the attacker just found your entire user database.

Step 2: Download

The attacker clicks the link. The browser downloads the file. Or they use curl or wget to save it. The file is now on their computer. Your entire user database. Every username. Every password. In plaintext.

Here is what a typical flat file database looks like:

admin:SuperSecret123
jane@email.com:mypassword
bob@company.com:bob2024!
sarah@startup.io:Welcome1

There is no hashing. No encryption. No salting. Just usernames and passwords sitting in a text file like a grocery list.

Step 3: Credential Stuffing

The attacker now has real email addresses paired with real passwords. Most people reuse passwords across multiple sites. The attacker feeds these credentials into automated tools that try them against Gmail, Microsoft 365, banking sites, Amazon and dozens of other services.

Your 50-user side project just became the entry point for compromising your users' entire digital lives. And you will never know it happened because you have no logging, no monitoring and no breach detection. The file was downloaded with a simple HTTP GET request that looks identical to normal traffic.

Step 4: Escalation

If any of those users have admin accounts on other systems, the attacker now has admin access to those systems. If any users reuse their password for work email, the attacker has access to corporate communications. If any users reuse their password for financial accounts, the attacker has access to money.

One text file. Minutes of effort. Potentially catastrophic consequences.

Why Server Misconfiguration Makes This Worse

You might think "my files are not in the public web directory." Maybe they are not. But server misconfiguration is extremely common in vibe-coded deployments. Common issues include:

Directory listing enabled: Visiting yoursite.com/data/ shows a list of every file in the directory. The attacker does not even need to guess file names.

Path traversal: A vulnerability in your app lets the attacker request files outside the web root using sequences like ../../data/users.txt. The AI-generated code that handles file paths may not sanitize this input.

Backup files exposed: The AI or your editor created users.txt.bak or users.json.old and those backup files are accessible even if the main file is protected.

Git repository exposed: If yoursite.com/.git/ is accessible, an attacker can reconstruct your entire source code and find the path to your data files regardless of where they are stored.

Plaintext vs. Hashed vs. Salted vs. bcrypt

Understanding the difference between these storage methods is critical for anyone building a login system.

Plaintext (what vibe-coded apps do)

password: MySecret123
stored as: MySecret123

If the database is stolen, the attacker has every password immediately. Zero effort required.

Simple Hash (MD5 or SHA-1 - still bad)

password: MySecret123
stored as: 5d41402abc4b2a76b9719d911017c592

This looks better but is not. Attackers use precomputed rainbow tables containing billions of hash-to-password mappings. Common passwords are cracked in milliseconds. MD5 and SHA-1 are not designed for password storage. They are designed to be fast. Fast is bad when an attacker is trying every possible password.

Salted Hash (better, but depends on the algorithm)

password: MySecret123
salt: a7f2b9c1 (unique random value)
stored as: salt + hash(salt + password)

A salt is a unique random value added to each password before hashing. This defeats rainbow tables because every password produces a different hash even if two users have the same password. But if the hashing algorithm is still fast (like SHA-256), the attacker can brute force the salted hashes with modern GPUs.

bcrypt (what you should use)

password: MySecret123
stored as: $2b$12$LJ3m4ks9Yxz.../encrypted_result

bcrypt is specifically designed for password storage. It is intentionally slow, which makes brute force attacks impractical. It includes a built-in salt. It has a configurable work factor that lets you increase the computation cost as hardware gets faster. bcrypt is the industry standard for password storage and is available in every major programming language.

Other acceptable alternatives include scrypt and Argon2. The point is: use an algorithm specifically designed for password hashing. Not a general-purpose hash. Not a text file.

What Directory Traversal Looks Like

Even if your password file is stored outside the web root, a directory traversal vulnerability can expose it. Here is how it works.

Your app has a feature that reads files, maybe for displaying user uploads or serving documents. The URL looks like:

yoursite.com/view?file=report.pdf

An attacker changes the request to:

yoursite.com/view?file=../../../data/users.txt

If the code does not validate and sanitize the file path, the server follows the ../ sequences up out of the web root and into your data directory. The attacker downloads your password file through a feature you built for something completely different.

AI-generated code frequently fails to sanitize file paths because path traversal protection was not part of the feature request. You asked for a file viewer. The AI built a file viewer. You did not ask for a secure file viewer.

How to Fix This

If you currently store passwords in flat files, here is what to do:

Step 1: Switch to a proper database. SQLite is the simplest option and comes built into most frameworks. PostgreSQL or MySQL for anything production-grade.

Step 2: Hash all passwords with bcrypt before storing them. Every modern programming language has a bcrypt library. In Node.js it is bcrypt. In Python it is bcryptjs or passlib. In PHP it is the built-in password_hash() function.

Step 3: Delete the old flat files. Permanently. Check your git history too. If the file was ever committed to version control, it is still accessible in the repository history.

Step 4: Force a password reset for all existing users. Their old passwords were stored in plaintext. You must assume they are compromised.

Step 5: Get a professional audit. Fixing password storage is just one issue. If your app stored passwords in a text file, there are almost certainly other security problems with your login system. Run through our 5-minute security checklist to find the most obvious ones, then schedule a professional penetration test to find the rest.

The Bottom Line

Flat file password storage is not a minor issue. It is a guarantee that a breach will expose every credential in your system with zero effort from the attacker. If you are a vibe coder using Cursor, Bolt, Lovable or any other AI tool, check your codebase right now. Search for files named users, accounts, credentials or passwords with extensions like .txt, .json or .csv.

If you find one, stop everything and fix it. If you are not sure how, Sherlock Forensics can audit your code and tell you exactly what to change. Quick audits start at $1,500 and we write remediation steps in plain language that you can paste directly into your AI coding tool.

Frequently Asked Questions

Where should I store user passwords?

User passwords should be stored in a proper database (PostgreSQL, MySQL or SQLite) using a strong hashing algorithm like bcrypt, scrypt or Argon2. Never store passwords in flat files. Never store them in plaintext. Every password must be individually salted before hashing.

Is a .json file safe for storing passwords?

No. A .json file provides no security for password storage. It stores data in plaintext, can be downloaded if the server is misconfigured and offers no hashing or encryption. Use a proper database with bcrypt hashing instead.

What is password hashing?

Password hashing is a one-way mathematical function that converts a password into a fixed-length string. It cannot be reversed. When a user logs in, the entered password is hashed and compared to the stored hash. This protects passwords even if the database is stolen because the attacker gets hashes, not actual passwords. Use bcrypt, scrypt or Argon2 for password hashing.

Can someone download files from my server?

Yes, if the server is misconfigured or if your application has a directory traversal vulnerability. Files stored in web-accessible directories can be requested directly by URL. Automated bots scan for common file paths constantly. A penetration test will identify whether your files are exposed.