Securing API Keys and Secrets in Your Codebase

Hardcoded API keys, database passwords, and authentication tokens are one of the most common security problems in software development. Despite being well-known, this issue appears in thousands of public repositories every day. This guide explains why it happens, what the real risks are, and how to systematically eliminate exposed secrets from your codebase.


Why Hardcoded Secrets Are Dangerous

The moment a credential is committed to a repository, it becomes part of the Git history. Even if you delete the file or overwrite the value in a later commit, the original secret remains in the history and can be recovered with a simple git log command.

For public repositories, the exposure is immediate. Automated bots continuously scan GitHub, GitLab, and Bitbucket for patterns that match API keys, tokens, and connection strings. Research from GitGuardian's 2024 State of Secrets Sprawl report found over 12.8 million new secret occurrences in public repositories in a single year.

For private repositories, the risk is different but still significant. Any developer with read access can see every credential. If the repository is ever made public, accidentally shared, or compromised through a supply chain attack, every secret in the history is exposed.


Common Exposure Patterns

Secrets end up in code through predictable patterns.

Configuration files are the most common source. Connection strings in appsettings.json, API keys in .env files committed by accident, or tokens in config.yaml that were meant to be placeholders but shipped to production.

Mobile applications are particularly vulnerable. API keys, backend URLs, and SDK tokens are compiled into the binary. Even without access to the source code, anyone can extract them from the APK or IPA using basic decompilation tools (see Protecting a Mobile App Before Publishing).

CI/CD pipelines often store secrets in environment variables, but those variables sometimes leak through build logs, error messages, or misconfigured pipeline configurations.

Third-party integrations frequently require API keys that developers hardcode during initial setup and never migrate to a secure store.


Step 1: Detect Existing Secrets with Radar

Before you can fix the problem, you need to know where your secrets are. ByteHide Radar scans your repositories for exposed credentials, API keys, tokens, and other sensitive patterns.

Radar supports detection of over 200 secret types including AWS keys, Stripe tokens, database connection strings, JWT secrets, OAuth tokens, private keys, and many more. See Supported Secret Types for the full list.

How It Works

Connect your GitHub repository to Radar through the Cloud Panel. Radar will scan every commit, branch, and pull request for secret patterns.

When Radar finds a secret, it shows you the exact file, line number, commit, and the type of secret detected. It also tells you if the secret is still active in the current branch or only present in the Git history.

You can also define Custom Detection Rules for internal tokens or proprietary credential formats that standard patterns do not cover.


Step 2: Replace Hardcoded Secrets with ByteHide Secrets

Detecting exposed credentials is the first half. The second half is replacing them with a proper secret management system.

ByteHide Secrets provides a centralized, encrypted store for all your application credentials. Instead of hardcoding values, your application references secret names that are resolved at runtime.

How It Works

  1. Store your credentials in the ByteHide Secrets vault through the Cloud Panel or SDK
  2. Replace hardcoded values in your code with secret references
  3. The Secrets SDK retrieves the values at runtime from the encrypted vault

Example: .NET

Before:

C#
var connectionString = "Server=prod-db.example.com;Password=s3cret!;";

After:

C#
using ByteHide.Secrets;

var connectionString = await SecretsManager.Get("database-connection-string");

See the full .NET Secrets SDK documentation for setup and configuration.

Example: JavaScript

Before:

JavaScript
const apiKey = "sk-live-abc123def456";

After:

JavaScript
import { SecretsManager } from '@bytehide/secrets';

const apiKey = await SecretsManager.get("stripe-api-key");

See the full JavaScript Secrets SDK documentation for setup and configuration.

Environment Management

Secrets supports multiple environments (development, staging, production) so you can use different values for each stage without changing your code. See Environment Management for details.


Step 3: Protect Secrets Embedded in Binaries with Shield

For mobile and desktop applications, some values must exist in the binary at runtime. Backend URLs, SDK initialization tokens, and configuration values cannot always be fetched from a remote server (the app needs to know where the server is in the first place).

ByteHide Shield encrypts all string constants in your compiled binary. Even if an attacker decompiles the APK or IPA, they cannot read the encrypted values through static analysis.

Defense in Depth

String encryption makes extraction significantly harder, but it is not a replacement for proper secret management. The best approach is to minimize the number of secrets embedded in the binary (use server-side token exchange when possible) and encrypt the ones that must be there.


The Full Picture

The strongest approach combines all three layers: detection, management, and protection.

Radar catches secrets before they reach production. Secrets replaces them with a managed, encrypted alternative. Shield protects the values that must be compiled into the application.


Practical Recommendations

Rotate compromised credentials immediately. If Radar finds an active secret in your repository, assume it has been compromised. Generate a new credential, update the Secrets vault, and revoke the old one.

Never commit .env files. Add .env, .env.local, and .env.production to your .gitignore. If they were committed previously, they are still in the Git history and the secrets should be rotated.

Use CI/CD secret injection. Instead of storing secrets in configuration files, inject them through your CI/CD pipeline's secret management (GitHub Secrets, Azure Key Vault, etc.) and use ByteHide Secrets for the application runtime.

Minimize secrets in mobile binaries. Use server-side token exchange patterns where the mobile app authenticates with a short-lived token and the server handles the actual API calls with the long-lived credentials.

Set up Radar on every repository. Secret scanning should run on every commit and every pull request. See Integrating Security Into Your CI/CD Pipeline for setup details.


Next Steps

Previous
Security in Your CI/CD Pipeline