Exposed files and secrets

A web server serving a file nobody linked to is the most common way source code and credentials leak. This page covers the twenty-nine files worth checking for, what an attacker does with each, and the fix. Almost all of them are introduced by a deploy rather than by an attack.


Contents

Why does a file nobody linked to still get found?

Nothing has to link to it. The paths below are the same on every install of the framework or tool that creates them, so they are guessed rather than discovered. Scanners request a few dozen well-known paths against every host they see, and a 200 response is the whole discovery step. This is why "it is not linked anywhere" has never been a control.

How do these files end up public in the first place?

Most often the document root is one directory too high. A framework that expects to serve from public/ will happily serve the application root instead if the web server is pointed there, and everything beside the code goes with it: the environment file, the dependency manifests, the version control directory. The second common cause is a deploy that copies the whole working tree instead of a build artifact.

Which of these actually matter?

Rank them by what they hand over. A readable .env or private key is a direct credential leak and should be treated as a live incident. A readable .git directory is the same thing one step removed, because the history usually contains secrets that were rotated out of the working tree and forgotten. Directory listings and phpinfo pages leak structure rather than secrets, which makes them reconnaissance rather than compromise, but they are still telling a stranger where to look next.

What does fixing one of these involve?

Two steps, and the order matters. Block the path at the web server or CDN so it stops being readable. Then rotate every credential that was reachable while it was, because you cannot tell who read it. Doing only the first leaves a working key in somebody else's notes. If the exposure was a version control directory, rotate anything that appears anywhere in the history, not only what is in the current files.

Every finding, and what it means

29 checks, each with what it is, why it matters and the fix. Every one has its own link, so a finding can be sent to whoever owns the fix without sending the whole page.

Your .git/config is readable

What it is
The Git configuration file at /.git/config is being served publicly. Its presence means the whole .git directory is very likely readable too.
Why it matters
From a readable .git directory, an attacker can usually reconstruct your entire repository, including every past commit. Secrets that were committed and later deleted are still recoverable.
How to fix it
Block /.git/ at the web server or CDN, then rotate every credential that has ever appeared anywhere in the repository history.

Your .git/HEAD is readable

What it is
The Git HEAD pointer is publicly readable, which is the standard signal that the whole .git directory is exposed.
Why it matters
Tools that reconstruct a repository from a web-exposed .git directory start with exactly this file. Source code and historical secrets follow.
How to fix it
Deny /.git/ at the web server or CDN, then rotate anything that has ever been committed.

Your .env file is readable

What it is
A .env file is being served to anyone who requests it.
Why it matters
This is where applications keep database passwords, API keys and signing secrets, in plain text. A readable .env is usually a full compromise of everything it names.
How to fix it
Move the application root out of the web root, deny dotfiles at the server, and rotate every secret in the file. Treat all of them as leaked.

An .env backup is readable

What it is
A backup copy of an environment file (.env.bak, .env.old or similar) is publicly readable.
Why it matters
Backups are excluded from deny rules far more often than the original, and they usually contain the same secrets, sometimes older ones that were never rotated.
How to fix it
Delete the backup from the web root and rotate the secrets it contains.

A private key is readable

What it is
A file containing a private key is being served publicly.
Why it matters
Depending on the key, this allows decrypting traffic, impersonating your server, or signing in as it. There is no partial version of this problem.
How to fix it
Remove the file immediately, then generate a new key pair and revoke the old one. Assume the key is compromised.

AWS credentials are readable

What it is
A file containing AWS access credentials is publicly readable.
Why it matters
Exposed cloud keys are found and used by automated scanners within minutes, typically for cryptomining or data exfiltration, and the bill lands on you.
How to fix it
Deactivate the key in IAM right now, before fixing the exposure. Then remove the file and review CloudTrail for use you did not authorise.

A database backup is readable

What it is
A database dump or backup archive is publicly downloadable.
Why it matters
A dump is the whole dataset: user records, password hashes, anything else the database holds. This is the shape most breach disclosures take.
How to fix it
Remove it from the web root and store backups outside it. Then work out how long it was reachable, because that determines your disclosure obligations.

Directory listing is on

What it is
The server returns a browsable index of files instead of a page.
Why it matters
It is not a vulnerability by itself, but it removes all guesswork: anything in that directory can be found and read without knowing its name.
How to fix it
Turn off autoindex (nginx) or Options -Indexes (Apache) and add an index file.

A phpinfo() page is public

What it is
A page calling phpinfo() is reachable.
Why it matters
It prints the full environment: paths, loaded modules, exact versions, and often environment variables including secrets. It is a map of the host for anyone planning an attack.
How to fix it
Delete the file. It is a debugging artefact that should never reach production.

Apache server-status is public

What it is
The mod_status page is readable without authentication.
Why it matters
It shows live request URLs, client addresses and server internals, which leaks both user activity and the shape of your internal routes.
How to fix it
Restrict /server-status to localhost or an internal network in the Apache config.

Stack traces are shown to visitors

What it is
The application returns a full stack trace on error rather than a generic message.
Why it matters
Traces disclose file paths, framework and library versions, and sometimes query fragments or credentials, which is most of what an attacker needs to pick a specific exploit.
How to fix it
Turn off debug mode in production and return a generic error page. Log the detail server-side.

A .DS_Store file is readable

What it is
A macOS .DS_Store file is being served.
Why it matters
It lists the names of every file in its directory, including the ones not linked from anywhere, which turns guessing hidden paths into reading them off a list.
How to fix it
Delete it, add .DS_Store to .gitignore, and deny it at the server.

docker-compose.yml is readable

What it is
Your Compose file is publicly readable.
Why it matters
It describes your whole service topology, internal hostnames, exposed ports and frequently environment secrets in plain text.
How to fix it
Remove it from the web root and rotate any credentials it names.

Your Dockerfile is readable

What it is
The Dockerfile is being served publicly.
Why it matters
It reveals base images and exact versions, build steps, and sometimes tokens passed as build arguments, which makes finding a known CVE in your stack trivial.
How to fix it
Remove it from the web root.

composer.json is readable

What it is
The PHP dependency manifest is publicly readable.
Why it matters
It lists every library and version you run, so an attacker can look up which of them has a known vulnerability instead of probing for one.
How to fix it
Deny it at the server. Serve only your public directory.

An .npmrc with a token is readable

What it is
An npm configuration file containing an auth token is publicly readable.
Why it matters
The token can publish packages as you. That is a supply-chain compromise of everyone who installs them, not just of you.
How to fix it
Revoke the token immediately, then remove the file from the web root.

An .htpasswd file is readable

What it is
The password file for HTTP basic auth is publicly readable.
Why it matters
It contains usernames and password hashes. Basic-auth hashes are often weakly formatted and crack quickly offline.
How to fix it
Deny .ht* files at the server, then change every password in the file.

web.config is readable

What it is
The IIS configuration file is being served rather than interpreted.
Why it matters
It commonly holds connection strings and machine keys. A leaked machine key allows forging authentication tokens.
How to fix it
Fix the handler mapping so .config files are never served, and rotate any secrets it contains.

appsettings.json is readable

What it is
The ASP.NET configuration file is publicly readable.
Why it matters
It typically contains connection strings, API keys and JWT signing secrets in plain text.
How to fix it
Remove it from the served directory and rotate every secret it names.

A wp-config backup is readable

What it is
A backup of wp-config.php is publicly readable. Backups are served as text where the original is executed as PHP.
Why it matters
wp-config.php holds your database credentials and WordPress authentication salts, so this is usually full database access.
How to fix it
Delete the backup, change the database password, and regenerate the salts.

.gitlab-ci.yml is readable

What it is
Your CI pipeline definition is publicly readable.
Why it matters
It describes your build and deploy process, internal registry and host names, and sometimes tokens, which is a map of how to reach your infrastructure.
How to fix it
Remove it from the web root and rotate anything it references.

A GitHub workflow file is readable

What it is
A file under .github/workflows/ is being served.
Why it matters
Same as any CI definition: it exposes your deploy process and the names of the secrets it uses. If the repository is private, this leaks what was meant to stay internal.
How to fix it
Deny /.github/ at the server.

A debug endpoint is exposing the environment

What it is
A debug route is returning environment variables.
Why it matters
Environment variables are where applications keep secrets. An endpoint that prints them is equivalent to a readable .env, but harder to notice.
How to fix it
Remove or authenticate the endpoint and rotate everything it printed.

Spring Actuator /env is public

What it is
The Actuator environment endpoint is reachable without authentication.
Why it matters
It dumps the full application environment including datasource passwords and API keys.
How to fix it
Restrict Actuator endpoints to an internal port, or secure them with Spring Security. Then rotate the secrets it exposed.

Spring Actuator heapdump is public

What it is
The /actuator/heapdump endpoint is downloadable.
Why it matters
A heap dump is a snapshot of live memory. Credentials, session tokens and user data are all recoverable from it with ordinary tooling. This is one of the most serious exposures on this list.
How to fix it
Disable the heapdump endpoint immediately, then rotate every credential the application holds and invalidate active sessions.

An API spec is publicly readable

What it is
An OpenAPI or Swagger document is reachable without authentication.
Why it matters
Not always a mistake, since many APIs publish their spec deliberately. When it is not deliberate, it hands over a complete list of your endpoints and parameters, including internal ones.
How to fix it
If it was not meant to be public, require authentication for it. If it was, make sure it documents only the endpoints you intend to expose.

An SVN working copy database is readable

What it is
The .svn/wc.db file is publicly readable.
Why it matters
It allows reconstructing the source tree, in the same way an exposed .git directory does.
How to fix it
Deny /.svn/ at the server and remove the directory from the deployment.

A Mercurial repository is readable

What it is
The .hg/requires file is publicly readable, indicating an exposed Mercurial repository.
Why it matters
Source code and commit history can be recovered from it.
How to fix it
Deny /.hg/ at the server and remove it from the deployment.

OpenID configuration is published

What it is
A standard .well-known/openid-configuration document is reachable.
Why it matters
This one is normal and usually intentional: the OpenID Connect spec requires it to be public. It is listed here only so you know what it is.
How to fix it
No action needed unless this host was not supposed to be running an identity provider at all.

Frequently asked

How long was it exposed before I found it?

Usually you cannot tell from the outside, and access logs are the only place that answer lives. Treat the exposure window as the whole time since the deploy that introduced it, because that is the honest assumption. This is the argument for checking on every deploy rather than occasionally.

Is a 403 on these paths good enough?

It is fine as the outcome, but check what produced it. A 403 from the web server because the path is denied is a control. A 403 from the application because it has no route is an accident that the next framework upgrade can undo. Deny the path explicitly.

Does blocking the path fix the leak?

It stops the leak continuing. It does nothing about what was already read, which is why rotation is not optional. A credential that was publicly readable is a credential you have to assume is in circulation.

Are these checks safe to run against my own site?

Yes. Every one of them is a plain GET of a well-known path, the same request a browser makes for a page. Nothing is written and nothing is exploited. Only run them against domains you own or are authorised to test.

Run this against a domain you own. Start free