Skip to content

Source code publicly accessible

CWE-527CWE-540CWE-200OWASP A05:2021Updated September 4, 20265 min read

If a .git folder, a backup file or an editor file ends up on the web server, an attacker can reconstruct your complete source code. That exposes not only your logic and internal endpoints, but also the passwords and keys ever recorded in the history and later removed.

An application normally protects itself partly because an outside attacker has to guess how it works. If the source code becomes publicly accessible, that guessing disappears entirely: they simply read along. And because version control keeps everything, they read along with the mistakes you have since fixed. Here is how that happens, and why cleaning up alone is not enough.

How does source code become public?

Source code becomes publicly accessible when files needed only during development end up in the directory your web server serves. There are a few recurring causes, and they share the fact that deployment happened by copying a directory rather than publishing a build.

The best known is the .git folder. It contains your project’s complete archive: every version of every file, all commit messages and all contributors’ names. If that folder sits under the web root and the web server does not block it, someone with a simple tool can reconstruct the whole project, as though they had cloned it themselves.

Then there are backup and editor files: config.php.bak, index.php~, .env.old, site.zip. Those arise from manual work on the server and lie around for years. The nasty part is that a file with a changed extension is no longer run by the interpreter but served as plain text: config.php shows nothing, config.php.bak shows your database password.

Think of an architect who, after handover, leaves the construction drawings, the key plans and every earlier design version lying in the hall. The building is finished and secure, but anyone walking in knows exactly where the weak points are.

How does an attacker retrieve your source code?

Vulnerable:

Deployment happens with a git pull in the web root. The structure on the server then looks like this:

/var/www/portal/
├── .git/               ← the complete archive
├── .env                ← database password, API keys
├── index.php
└── config.php.bak      ← manual backup from last week

An attacker first checks whether the archive is reachable:

GET /.git/HEAD HTTP/1.1
Host: portal.example

HTTP/1.1 200 OK
ref: refs/heads/main

That response is enough. With a tool that fetches the objects from .git, they reconstruct the complete project including its history. Then they search that history:

$ git log -p --all | grep -iE "password|api_key|secret"
+  DB_PASSWORD=Summer2019!
+  STRIPE_SECRET_KEY=sk_live_51H8xQ2...

These keys were taken out of the code two years ago but still sit in the archive. And if the database key was never changed at the time, it still works.

Safe:

The structural fix is not deploying from a working directory. Publish a built artefact, or check out to a directory outside the web root and point the web server at a subdirectory of it:

/opt/portal/             ← code, outside the web root
├── .git/
├── .env
└── public/              ← only this is the web root
    ├── index.php
    └── assets/

As an additional layer, and purely as a safety net, block hidden folders and backup extensions in the web server:

# Never serve, even if something ends up there by accident
location ~ /\.(git|svn|hg|env) {
    deny all;
    return 404;
}
location ~ ~$|\.(bak|old|orig|save|swp|zip|tar\.gz)$ {
    deny all;
    return 404;
}
If this has already happened, regard every secret that ever sat in the history as leaked and rotate it: database passwords, API keys, signing keys, tokens. Removing the folder fixes the leak, but not the keys already fetched. Also check your access logs for requests to .git paths.

What is the impact of publicly reachable source code?

The severity is high to critical, because of the combination of two things: complete knowledge of the application and direct access to secrets.

The knowledge alone weighs heavily. With the source code, an attacker sees exactly how authorisation is set up, which endpoints exist that appear nowhere in the interface, how tokens are generated, and where the checks sit that they must bypass. Every other vulnerability in the application thereby becomes considerably easier to find and exploit.

The secrets make it acute. Configuration files in practice contain database passwords, keys for payment services, tokens for email services and the key used to sign session tokens. That last one is particularly serious: whoever holds the signing key can create valid tokens for any user, including administrators, without ever having to guess a password.

What makes it extra difficult is the reach in time. The archive contains not only what is in it now, but everything that has ever been in it. A key accidentally recorded three years ago and neatly removed is still readable.

How do you detect publicly reachable source code?

Checking the best-known paths is quickly done: /.git/HEAD, /.git/config, /.env, /.svn/entries. A response with content instead of a 404 is an immediate hit. One important detail: a 403 means the folder exists but is being blocked, which indicates the code is in the web root and the protection depends solely on a configuration rule.

Beyond that, testers look for files not part of the application but present anyway. Candidates are derived from known filenames: for every page.php, variants such as page.php.bak, page.php~ and page.php.old are tried, and archives with predictable names such as backup.zip or site.tar.gz are sought. They also check whether directory listing is enabled, because then no guessing is needed at all. JavaScript source maps get attention too: a published .map file returns the original frontend code including comments. AssistSec covers this as standard in the reconnaissance phase of a penetration test, because a find here fundamentally changes the rest of the assessment.

How do you prevent publicly reachable source code?

  • Deploy with a built artefact or an export, never by copying a working directory with version control.
  • Put the code outside the web root and point the web server only at a public subdirectory.
  • Block hidden folders and backup extensions in your web server as a safety net.
  • Keep secrets out of the code and in environment variables or a key vault.
  • Rotate every secret that has ever been in version control, even if it was later removed.
  • Run a secrets scanner in your development pipeline, so keys are not recorded again.
  • Do not publish JavaScript source maps in production, or restrict access to them.
  • Never make manual backups in the web root; use a separate location for that.
  • Check periodically what is actually in the web root rather than what should be.

Sources

Frequently asked questions

Is blocking the .git folder in the web server enough?

It is a good stopgap, but not a structural fix. The folder should not be on a production server at all; publish a checked-out copy or a build artefact instead of the working directory. A blocking rule can quietly disappear during a migration or on a new server.

Why is a removed password still a problem?

Because Git keeps the full history. A key that appeared in one commit and was taken out in a later one still sits in the archive and can be retrieved with a single command. Removing it from the current version changes nothing about that.

Which other files leak source code?

Backups and editor files are the best known: file.php.bak, file.php~, vim .swp files, and archives such as site.zip or backup.tar.gz. Also .svn and .hg folders, and configuration files such as .env. The cause is usually that the whole working directory was copied.

What should I do if this has happened?

Assume every key, password and token that ever sat in the history has leaked. Rotate them all, whether or not they appear to be in use. Only then remove the folder, and check your access logs to see who fetched the files.

Related articles

Press / to search · Esc