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.jsoninto a directory that the web server hands out as static content, sohttps://yoursite/appsettings.jsonreturns 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.bakor 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.jsonat 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.jsonlives 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.