Iโll be honest, when I first started working with .NET, I didnโt think much about secrets management. Hardcoding an API key or a database connection string in a config file felt harmless. But one day, I accidentally pushed a repository with a hardcoded key to GitHub. Within minutes, bots had scanned it, and the key was compromised.
That was my wake-up call. I quickly learned that secrets leakage is one of the most common security mistakes developers makeโand attackers know it. They constantly scan public and private repositories looking for credentials that could grant them access to systems, databases, or third-party APIs.
โ ๏ธ If you already know why it is necessary and all the theoretical part, skip this and you can:
But hereโs the thing: even if your repository is private, your secrets arenโt necessarily safe.
Reverse Engineering: A Hidden Risk
Attackers donโt always need access to your source code to steal secrets. With reverse engineering, they can decompile compiled applications and extract credentials directly from binaries.
- .NET assemblies (.dll, .exe) can be decompiled with tools like dnSpy, ILSpy, or JetBrains dotPeek to reveal hardcoded secrets.
- Mobile applications (.apk) can be decompiled and inspected to extract API keys and tokens.
- Web applications can expose secrets through client-side JavaScript or misconfigured APIs.
Even if you never push a secret to a repository, if itโs embedded in your application, it can still be stolen.
How Do We Prevent This?
- Detect secrets before they get into source control or production.
- Automatically extract secrets and replace them with secure references.
- Store and manage secrets in an encrypted vault.
In this guide, Iโll walk you through how to detect secrets in your .NET project at build time, extract them securely, and manage them properly using industry best practices.
Secret Detection in .NET During Build Time
Hardcoded secrets are one of the biggest security risks in software development. Whether it’s an API key, database connection string, or cryptographic token, leaving sensitive credentials in your source code can lead to serious vulnerabilities.
Many developers assume that keeping their repositories private is enough, but that’s not the case. If a secret is present in your code, it can still be leaked through:
- Accidental pushes to public repositories.
- Code reviews and log files.
- Reverse engineering of compiled binaries.
Why Detect Secrets During Build Time?
Once a secret is committed to a repository or embedded in a compiled application, itโs already at risk. Thatโs why detecting secrets earlyโduring the build processโis essential.
By implementing automatic secret detection in your CI/CD pipeline, you can:
- Prevent secrets from being pushed to version control.
- Ensure that compiled binaries do not contain sensitive information.
- Reduce the risk of exposing credentials in production environments.
Security best practices recommend integrating automated tools into your build process to identify and eliminate secrets before they become a problem.
Tools for Secret Detection in .NET
Several tools can help detect secrets automatically:
- Online Secrets Sprawl โ Scans repositories to detect exposed secrets, even in private repos.
- ByteHide Secrets โ Integrates into your project to detect secrets every time the code is compiled.
- GitHub Secret Scanning โ Detects exposed credentials in repositories and alerts developers.
- TruffleHog โ Uses pattern matching and entropy analysis to detect secrets in codebases.
The best way to prevent hardcoded secrets is to automate their detection while coding. In the next section, Iโll show you how to integrate this directly into your .NET project so that secrets are flagged every time you compile.
Detecting Secrets in .NET automatically with MSBuild
Alright, we’ve talked about why detecting secrets is crucial, but now it’s time to actually implement it in our .NET project. The goal here is simple: every time we compile our application, secrets should be detected automatically.
Weโre going to set up ByteHide Secrets so that it integrates directly into our build process, alerts us when secrets are found, and even allows us to define actions when a secret is detected.
Letโs break it down step by step.
0. Setup your project
Yes, 0. we are developers.
You must do it in your real project, but, for this article i’ll setup a sample project with a harcoded secret ๐
1. Install Secrets Scanning in Your Project
The first thing we need to do is install the ByteHide Secrets Integration package in our .NET project. Open a terminal in your projectโs root directory and run:
dotnet add package Bytehide.Secrets.Integration
This will add the package and configure it to work with MSBuild.
2. Get Your Free Project Token
To link our project with ByteHide and track detected secrets in a central panel, we need an API token.
Follow these steps:
- Go to ByteHide and create a free account.
- In the dashboard, create a new
.NET
project and selectSecrets
.
- Copy the Project Token provided after creating the project.
This token allows ByteHide to monitor detected secrets, send alerts, and apply custom actions.
3. Create the Configuration File
Now that we have our project token, we need to create a configuration file inside our project.
Create a new file in the root of your project and name it:
secrets-config.json
Open the file and add the following content:
{
"Name": "My Configuration",
"ProjectToken": "YOUR_PROJECT_TOKEN",
"Environment": "develop"
}
Replace YOUR_PROJECT_TOKEN
with the token you copied earlier.
4. Customize the Detection Behavior
ByteHide Secrets allows us to customize how secrets are detected and handled. Here are some useful settings you can modify in secrets-config.json
:
- RunConfiguration: Defines when secret detection should run. Default is
*
(runs in all cases). - Enabled: Set to
false
if you want to temporarily disable detection. - DisplayCode: If
true
, stores an extract of the detected secret in the ByteHide panel. - ThrowOnError: If
true
, stops compilation when an error occurs. - Actions: Configure what happens when a secret is found (e.g., email alerts, webhook notifications).
Example configuration:
{
"Name": "Production Build",
"ProjectToken": "YOUR_PROJECT_TOKEN",
"Environment": "production",
"RunConfiguration": "*",
"Enabled": true,
"DisplayCode": true,
"ThrowOnError": false
}
5. Run the Build Process (and scan secrets)
With everything set up, let’s compile our project:
Click on compile:
or:
dotnet build
If ByteHide Secrets detects any hardcoded credentials, it will:
- Stop the build process (if
ThrowOnError
is enabled). - Log the detected secrets.
- Send alerts (if email or webhook actions are configured).
And it will appear in the security panel:
Now that secret detection is automated, we can focus on securing them properly. ByteHide Secrets also offers an integrated panel where you can:
- View detected secrets.
- Manage secret replacements.
- Configure advanced protection settings.
In the next section, we’ll go a step further and see how to automatically extract and store secrets securely, rather than just detecting them.
Extracting and Storing Secrets Securely
Detecting secrets in your code is a great first step, but detection alone isnโt enough. Once a secret is identified, we need to extract it and store it securelyโotherwise, it remains a security risk.
Instead of leaving secrets hardcoded in our applications, we can use ByteHide Secrets to automatically extract and manage them, ensuring they never get exposed in the first place.
Let’s go through the process step by step.
1. Why Extract Secrets?
Secrets embedded in source code or compiled binaries can be accessed through:
- Version control (e.g., Git commits that store API keys by mistake).
- Decompilation (attackers can reverse-engineer your application to extract secrets).
- Logs and debugging tools that might print sensitive values.
By extracting secrets, we keep them out of our codebase and manage them in a secure environment.
2. Enabling Automatic Secret Extraction
Once ByteHide Secrets is installed and configured in our project, we can enable automatic secret extraction.
To do this, add the following option to your secrets-config.json
file:
{
"Name": "Production Secrets",
"ProjectToken": "YOUR_PROJECT_TOKEN",
"Environment": "production",
"export": {
"enabled": true,
"encrypt": true,
"prefix": "secret_"
}
}
Hereโs what each setting does:
- enabled: If set to
true
, secrets will be extracted and stored in ByteHide’s cloud. - encrypt: If set to
true
, the ID of extracted secrets will be encrypted in the binary (This is a extra-security feature to combat reversing analyzers) . - prefix: Defines a prefix for replaced secrets, making it easier to track them.
3. Running the Extraction Process
With extraction enabled, all we need to do is build our project:
dotnet build
ByteHide Secrets will scan our application, detect sensitive data, and replace it with a reference.
So, in the code, the hardcode secret will be replaced with the secure vault reference of this secret,
Before:
After:
Cool! Now the secret is not anymore in the code/binary and it’s sync in our dashboard, so:
4. Where Are the Extracted Secrets Stored?
Extracted secrets are stored in the ByteHide Secrets Manager, where they can be accessed securely.
From the panel, you can:
- View and manage detected secrets.
- Assign environment-specific values (e.g., different API keys for dev, staging, and production).
- Apply role-based access control (RBAC) to manage who can see or use secrets.
Managing Secrets During Development
Ideally, secrets should never be hardcoded in the first place. If we set up proper secret management from the start, we wonโt need to rely on automatic extraction. However, whether we extract them automatically or manage them manually, the security level remains the same.
The key difference is that if secrets are not handled correctly in the code, they can still end up in version control (e.g., Git) before being detected. Letโs look at two ways to manage secrets correctly during development.
1. Marking Secrets Manually
Sometimes, secrets may not be detected automaticallyโthis could be because they are very simple (e.g., a password for encrypting a .zip file) or follow an unconventional pattern. In these cases, we can explicitly mark them for protection.
First, install the ByteHide ToolBox package:
dotnet add package Bytehide.ToolBox.Secrets
Then, use the ThisIsASecret
method to manually mark the secret:
using System;
using Bytehide.ToolBox.Secrets;
namespace Hollow
{
internal class Program
{
private static void Main()
{
var password = SecretsMarker.ThisIsASecret("drowssap", "my_db_password");
var database = new DB.Database("Hollow", password);
database.CreateDatabase();
Console.ReadKey();
}
}
}
Alternatively, we can use the IsASecret
extension method:
var password = "drowssap".IsASecret("my_db_password");
If we prefer, we can also use an attribute to mark a class-level secret:
using System;
using Bytehide.ToolBox.Secrets;
namespace Hollow
{
internal class Program
{
[ThisIsASecret("my_db_password")]
private static readonly string _password = "drowssap";
public static void Main()
{
var database = new DB.Database("Hollow", _password);
database.CreateDatabase();
Console.ReadKey();
}
}
}
2. Managing Secrets Securely with ByteHide Secrets SDK
The best way to handle secrets is to store them securely from the beginning. Instead of writing secrets in the code, we can:
- Go to your project in the Secrets Manager and create a new secret.
- Click on “Create secret”
- Add the name, the value and select your environment. Click on “Create”
- Now, you will see the secret under “keys” tab.
Cool, go now to our visual studio project,
- Install the ByteHide Secrets SDK in our application:
dotnet add package Bytehide.ToolBox.Secrets
Configuring the Secrets Manager Securely
To securely interact with ByteHide Secrets from the SDK, we need to provide our API Key. Since this key grants access to our secrets, it should never be hardcoded. Instead, we have three options:
- โ Secure (Recommended): Store the API Key in an environment variable.
- โ ๏ธ Acceptable: Store the API Key in
app.config
. - โ Insecure (Not Recommended): Hardcode the API Key in the source code.
Option 1: Using Environment Variables (Recommended)
We can set the API Key as an environment variable in our operating system:
export BYTEHIDE_SECRETS_TOKEN=e34762f8-8ad6-0000-0000-000000000000
Then, in our application, initialize the secrets manager:
namespace Sample;
internal class Program
{
internal static void Main()
{
// Initialize the secrets manager (API Key is automatically retrieved from the environment variable)
Bytehide.ToolBox.Secrets.ManagerSecrets.Initialize();
var secrets = Bytehide.ToolBox.Products.Secrets;
// Get a secret from ByteHide Secrets Manager
var value = secrets.Get("mongo-db-password");
Console.WriteLine($"My secret is: {value}");
Console.ReadLine();
}
}
Option 2: Using app.config
If using environment variables is not an option, we can store the API Key in app.config
:
<configuration>
<appSettings>
<add key="ByteHide.Secrets.Token" value="e34762f8-8ad6-0000-0000..."/>
</appSettings>
</configuration>
Then, initialize the secrets manager using:
Bytehide.ToolBox.Secrets.ManagerSecrets.Initialize(true);
Option 3: Hardcoding the API Key (Not Recommended)
For testing purposes only, we can directly set the API Key in code. This method is highly discouraged for production environments:
Bytehide.ToolBox.Secrets.ManagerSecrets.UnsecureInitialize("development", "e34762f8-8ad6-0000-0000...");
Getting the secrets in our code
Once our SDK is initialized, the recommendation is to do it in the methods that are executed first in our application.
Now, we can get the secrets wherever we want in a secure way, such as before connecting to a database:
var secrets = Bytehide.ToolBox.Products.Secrets;
// Get a secret from ByteHide Secrets Manager
var value = secrets.Get("mongo-db-password");
mongoDB.connect("127.0.0.0", value);
And there we have it ๐! Secrets that are updated from the panel will be synchronized in our code.
In addition, it performs several security checks to avoid network sniffing, reverse engineering and credential theft.
Now let’s see how we can add new secrets from the code in our cloud manager.
3. Creating Secrets Programmatically
Instead of manually creating secrets in the panel, we can also create them dynamically from our application:
namespace Sample;
internal class Program
{
internal static void Main()
{
Bytehide.ToolBox.Secrets.ManagerSecrets.Initialize();
var secrets = Bytehide.ToolBox.Products.Secrets;
// Create a new secret
secrets.Set("new-secret", "secure-value");
Console.WriteLine("Secret successfully created.");
Console.ReadLine();
}
}
Now let’s explore how to securely share secrets across teams without exposing them in plaintext.
Securely Sharing Secrets Across Teams
When working in a development team, sharing secrets like API keys, database credentials, or encryption keys can become a challenge. Sending secrets over Slack, email, or even embedding them in a shared repository poses a serious security risk.
Instead of handling secrets manually, we can use ByteHide Secrets Manager to securely share and manage access to secrets across teams.
Letโs explore two ways to do this.
1. Managing Teams in the Secrets Manager
The most secure way to share secrets with your team is to manage access directly within the Secrets Manager. This allows for:
- Role-based access control (RBAC) to define who can view or modify secrets.
- Environment-specific secrets, so teams can use different credentials for development, staging, and production.
- A centralized audit log to track who accessed or modified a secret.
How to Set Up Team Management
Once set up, team members can retrieve secrets directly from the ByteHide panel or through the SDK without needing to manually exchange sensitive information.
- Go to ByteHide and log in.
- Navigate to the Teams section and create a new team:
- Add team members by entering their email addresses or current organization members.
- Also Assign roles and permissions:
- Owner: Full access to secrets and team management, can remove secrets, vaults and projects.
- Manager: Can read/add secrets and modify them.
- Member: Can only view specific secrets.
- Now, with our new team created:
- Create a new project or open an existing one (update) and add the team to the “Teams” of the project.
We now have under control who can, who can’t, and the permissions on secrets within our development team.
What if it’s a simple secret that we don’t want to keep in the manager? Or share it with someone external? Let’s get to it.
2. Securely Sharing Secrets Without Adding Users
Sometimes, you need to share a secret with someone who isnโt part of your team, like a freelancer or an external service. Instead of sending secrets via insecure channels, we can use ByteHide Secure Sharing.
How to Share a Secret Securely
- Go to ByteHide Secure Sharing.
- Paste the secret you want to share.
- Set an expiration time (e.g., 1 minutes, 12 hour, 24 hours).
- Choose the number of times the secret can be viewed before it is deleted.
- Generate a secure shareable link.
- Send the link to the recipient.
Once the recipient views the secret, it will be automatically deleted, ensuring it cannot be accessed again.
What’s Next?
By now, weโve covered everything you need to detect, extract, and manage secrets securely in your .NET projects. But securing secrets isnโt just a one-time task, itโs an ongoing process.
Preventing secrets from leaking into your codebase, automating their detection during builds, and managing them properly across different environments and teams are essential steps to keeping your applications secure.
Now itโs your turn! Start implementing these strategies in your projects and take control of your secrets before they become a security risk.
Frequently Asked Questions (FAQ)
What are hardcoded secrets in .NET, and why are they a security risk?
Hardcoded secrets are sensitive data, such as API keys, database credentials, and encryption keys, that are directly embedded in a project’s source code. These secrets pose a security risk because they can be accidentally leaked through version control (e.g., GitHub repositories) or extracted using reverse engineering techniques.
How can I find hardcoded secrets in my .NET application?
To detect hardcoded secrets in .NET projects, you can use automated tools such as TruffleHog, Detect Secrets, or integrate secret detection into your CI/CD pipeline with ByteHide Secrets. These tools scan source code and configuration files to identify exposed secrets.
How do I prevent secrets from being pushed to Git repositories?
To avoid committing secrets to Git, you can:
- Add sensitive files to
.gitignore
. - Use Git Secrets to block commits containing sensitive information.
- Implement pre-commit hooks to scan for secrets before pushing code.
- Store secrets in environment variables or a secrets manager instead of hardcoding them.
What is the best way to store API keys securely in a .NET application?
The best practices for storing API keys securely in .NET include:
- Using a secrets manager like AWS Secrets Manager, Azure Key Vault, or ByteHide Secrets Manager.
- Storing API keys in environment variables instead of hardcoding them.
- Using the .NET
UserSecrets
feature for local development.
How do I use .NET user secrets for local development?
.NET provides the User Secrets feature to store sensitive data outside of source control. To use it:
- Run
dotnet user-secrets init
in your project root. - Add secrets using
dotnet user-secrets set "ApiKey" "your-secret-key"
. - Retrieve secrets in code using
Configuration["ApiKey"]
.
How do I encrypt and store sensitive data in .NET?
You can encrypt sensitive data in .NET using:
- AES Encryption for secure data storage.
- Protecting configuration files with ASP.NET Data Protection.
- Storing secrets in a secure cloud service like Azure Key Vault or ByteHide Secrets Manager.
What is secret sprawl, and how can I prevent it?
Secret sprawl occurs when sensitive credentials are scattered across multiple locations, making them difficult to track and secure. To prevent secret sprawl:
- Use a centralized secrets manager.
- Implement access controls and audit logs.
- Scan repositories regularly for leaked secrets.
How can I detect exposed secrets in my Git repository?
You can scan Git repositories for exposed secrets using tools like:
- ByteHide Secrets Sprawl โ Detects exposed secrets in public and private repositories.
- Detect Secrets โ A pre-commit hook for detecting secrets.
- Gitleaks โ Scans Git repositories for sensitive data.
How do I securely share secrets with my development team?
To securely share secrets with a team, avoid sharing credentials via chat apps or emails. Instead, use:
- A role-based access control system in a secrets manager.
- ByteHide Secure Sharing for expiring, one-time secret links.
What happens if a secret is leaked in my application?
If a secret is leaked, follow these steps immediately:
- Revoke the exposed credential (e.g., rotate API keys or change passwords).
- Remove the secret from source control using
git filter-branch
or BFG Repo Cleaner. - Implement automated secret detection to prevent future leaks.
What Do You Think?
Weโve covered everything from detecting hardcoded secrets to securely storing and sharing them in .NET applications. Implementing these best practices can significantly improve your applicationโs security and prevent accidental leaks.
Now, Iโd love to hear from you! Have you faced issues with secrets management in your projects? Do you already use a secrets manager, or are you still handling credentials manually?
Drop a comment below and let me know your thoughts! If you have any questions, Iโll be happy to help.