Exposed Files

appsettings.json & secrets.json: the .NET secrets you did not mean to ship

Where .NET keeps its secrets

If your backend is .NET, its configuration almost certainly lives in JSON. appsettings.json and its environment-specific siblings — appsettings.Development.json, appsettings.Production.json — are the standard home for settings, and during development the user-secrets feature uses a secrets.json file outside the project. These files routinely contain connection strings, third-party API keys, SMTP credentials and signing keys. That is fine while they stay where they belong. The risk appears when one ends up somewhere the public web can reach.

How they end up exposed

The exposure is almost never intentional. The usual paths are:

  • Copied into a served folder. A build or deploy step places appsettings.json into a directory that the web server hands out as static content, so https://yoursite/appsettings.json returns the file.
  • Committed and deployed. A config file with real secrets is committed, then the whole repo is deployed into a public path, or the repo itself is public.
  • Wrong content root. A misconfigured static-file setup serves the application directory itself, exposing every file in it.
  • Backup and leftover copies. An appsettings.json.bak or an editor swap file left next to the original and served the same way.

In each case the file is reachable by guessing one obvious path — and appsettings.json is about as guessable as a path gets.

Why this is worse than a single leaked key

A config file is a bundle. One exposed appsettings.json can hand over a database connection string, a mail credential, and several API keys at once — everything the app needs to run, in one request. That makes config exposure higher-impact than a single stray key, even though the cause is just as mundane. And because the filename is a convention rather than a secret, an automated probe does not have to guess cleverly — it just asks for the file by its standard name and sees what comes back.

How to keep them off the wire

The goal is two-fold: keep secrets out of files that could ship, and keep config files out of public paths.

  • Do not put production secrets in appsettings.json at all. Use environment variables, a secrets manager, or your platform's configuration store for anything sensitive. The committed file should hold non-secret defaults only.
  • Use user-secrets for local dev only. secrets.json lives outside your project for exactly this reason — keep it there and never in source control.
  • Serve static files from a dedicated folder, not the application root, so config files are physically outside the served directory.
  • Verify reachability. After deploy, confirm that the obvious config URLs return a 404, not your settings.

Check what is reachable

The hard part is that everything looks fine from the rendered site — an exposed config file is invisible until someone requests it directly. An automated scan probes the predictable paths a bot would try, including common .NET config and backup filenames, and reports anything that responds. Scan your site and make sure your settings are not one URL away.

Related reading

FAQ

Is it safe to put secrets in appsettings.json?
Not for production. Keep only non-secret defaults in appsettings.json and move connection strings and keys to environment variables or a secrets manager, so a copied or exposed file does not hand over live credentials.
What is secrets.json in .NET?
It is the file used by the .NET user-secrets feature for local development. It lives outside your project directory on purpose so it is not committed or deployed, and it should never be checked into source control.
How would appsettings.json get exposed publicly?
Usually a deploy step copies it into a folder served as static content, or the app serves its own root directory. Then the file is fetchable at a predictable URL like /appsettings.json. An automated scan checks those paths for you.