Examine code secrets

Completed

Modern applications depend on credentials to access databases, APIs, cloud services, and other external integrations. When developers hard-code these secrets directly in source code, they create a critical security vulnerability that can lead to data breaches, financial losses, and compliance violations.

What are code secrets?

Code secrets are sensitive credentials and authentication tokens that applications use to access protected resources. Unlike security vulnerabilities in code logic (like SQL injection), secrets are valid credentials that, if exposed, grant attackers legitimate access to systems and services.

Think of secrets as the keys to your application's external services. Just as you wouldn't attach your house key to the outside of your door, you shouldn't hard-code credentials in source files that could be exposed through version control, logs, or public repositories.

Common types of secrets

Different applications require different types of secrets based on their architecture and dependencies. Understanding what constitutes a secret helps you identify them in your codebase.

API keys

API keys authenticate your application to external services and platforms.

Examples include:

  • Payment gateways: Stripe API keys, PayPal client secrets, Square access tokens.
  • Cloud platforms: Azure subscription keys, Amazon Web Services (AWS) access keys (and secret access keys), and Google Cloud API keys.
  • Communication services: SendGrid API keys, Twilio auth tokens, Slack webhooks.
  • Analytics and monitoring: Google Analytics tracking IDs, Datadog API keys, Application Insights instrumentation keys.

Here's an example of a hard-coded API key vulnerability:

// DANGEROUS: Hard-coded Stripe API key
public class PaymentProcessor
{
    private const string StripeKey = "sk_live_51Abc123XYZ789..."; // Real secret exposed
    
    public async Task<bool> ProcessPayment(decimal amount)
    {
        StripeConfiguration.ApiKey = StripeKey;
        // Process payment...
    }
}

If this code is committed to a repository, the Stripe key becomes visible to anyone with repository access. In public repositories, the Stripe key is immediately visible to the entire internet.

Database connection strings

Connection strings contain credentials for accessing databases and often include multiple sensitive components.

A typical connection string includes:

// DANGEROUS: Hard-coded connection string with credentials
string connectionString = "Server=myserver.database.windows.net;" +
                         "Database=ProductionDB;" +
                         "User ID=admin;" +
                         "Password=MySecretP@ssw0rd123;" +
                         "Encrypt=true;";

This single string exposes:

  • Server hostname.
  • Database name.
  • Administrator username.
  • Administrator password.

An attacker with this information could directly access the database, bypass all application-level security, and potentially extract, modify, or delete all data.

Private keys and certificates

Private keys for encryption, signing, and TLS certificates must never appear in source code.

Examples include:

  • RSA private keys for encryption.
  • SSH private keys for server access.
  • JWT signing keys for token authentication.
  • TLS certificate private keys.
// DANGEROUS: Hard-coded private key
private const string JwtPrivateKey = @"-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7...
-----END PRIVATE KEY-----";

Private keys in source code compromise the security of everything they protect, potentially enabling attackers to decrypt data, impersonate your application, or access servers.

OAuth tokens and client secrets

OAuth credentials enable your application to act on behalf of users or access protected APIs.

Types include:

  • OAuth client IDs and secrets.
  • Personal access tokens (PATs) for services like GitHub, GitLab.
  • Service principal credentials for Microsoft Entra ID.
  • Refresh tokens with long-lived access.
// DANGEROUS: Hard-coded OAuth credentials
public class GitHubService
{
    private const string ClientId = "Iv1.abc123def456";
    private const string ClientSecret = "1234567890abcdef1234567890abcdef12345678";
}

These credentials could allow attackers to access user data, perform actions as your application, or impersonate users.

Encryption keys

Symmetric encryption keys used to protect data at rest or in transit must be kept secret.

// DANGEROUS: Hard-coded encryption key
private static readonly byte[] EncryptionKey = Convert.FromBase64String(
    "YourBase64EncodedKeyHere123456789ABCDEF=="
);

If an encryption key is exposed, all data encrypted with that key becomes vulnerable to decryption.

Why are exposed secrets dangerous?

The security implications of exposed secrets extend far beyond theoretical risks. They represent direct pathways for attackers to compromise systems, data, and operations.

Immediate and unrestricted access

Unlike exploiting code vulnerabilities that require technical skill and specific conditions, exposed secrets provide instant, legitimate access to protected resources. An attacker doesn't need to bypass security measures because they have valid credentials.

Consider the consequences:

  • Database credentials: Direct access to read, modify, or delete all database contents.
  • Cloud service keys: Ability to spin up expensive resources, access storage, or modify configurations.
  • Payment API keys: Unauthorized charges, refunds, or access to customer payment data.
  • Email service tokens: Sending spam or phishing emails from your domain.

Persistence and difficulty of revocation

Once a secret is exposed, the damage persists until the credential is rotated (changed). There are several problems that complicate revocation:

  • Git history: Even if you remove a secret from current code, it remains in the repository's commit history.
  • Forked repositories: In public repositories, other developers can fork your code with the secret intact.
  • Cached or archived versions: Search engines, archive services, or internal caches can capture the exposed secret.
  • Unknown exposure window: You might not know when the secret was first exposed or who accessed it.

The only secure response to an exposed secret is to immediately revoke it and generate a new one, then update all applications that use it. Remediation can be disruptive and time-consuming.

Compliance and regulatory consequences

Many regulatory frameworks explicitly require protecting credentials and sensitive authentication data:

  • Payment Card Industry Data Security Standard (PCI DSS): Payment Card Industry standards prohibit storing authentication credentials in clear text.
  • Health Insurance Portability and Accountability Act (HIPAA): Healthcare data protection requires safeguarding access credentials.
  • Service Organization Control 2 (SOC 2): Security audits verify that credentials are properly managed and protected.

Exposed secrets can result in failed audits, regulatory fines, and loss of certifications required to operate in certain industries.

Real-world scenarios

Understanding how exposed secrets translate to real breaches might help you appreciate the urgency of protecting them.

Scenario 1: Public repository exposure

A developer commits code to a public GitHub repository with a hard-coded AWS access key. Within hours:

  1. Automated bots scanning GitHub detect the key.
  2. Attackers use the key to spin up expensive EC2 instances for cryptocurrency mining.
  3. The company receives a $50,000 AWS bill within 24 hours.
  4. The key must be revoked, breaking all legitimate services using it.
  5. The incident requires security investigation, incident response, and potential legal notifications.

This scenario occurs regularly. GitHub's push protection feature prevents these incidents.

Scenario 2: Database credential exposure

A connection string with production database credentials is committed to an internal repository. A contractor with repository access:

  1. Extracts the connection string from the code.
  2. Connects directly to the production database.
  3. Exports customer personal information.
  4. The breach goes undetected for weeks because the access appears legitimate.

Application-level logging and security controls are bypassed entirely because the attacker has valid database credentials.

Scenario 3: API key in mobile app

An API key is hard-coded in a mobile application. Security researchers decompile the app and:

  1. Extract the API key from the compiled code.
  2. Use it to access the API without rate limiting or usage tracking.
  3. Enumerate all user data accessible through the API.
  4. The company must revoke the key, forcing all users to update the app.

Mobile and client-side code is vulnerable because attackers can analyze the compiled application at their leisure.

Best practices for managing secrets

Protecting secrets requires implementing secure patterns throughout the development lifecycle.

Never hard-code secrets

The fundamental rule: secrets don't belong in source code, configuration files tracked by version control, or client-side code.

Instead of hard-coding secrets, implement one of the following approaches:

  • Environment variables.
  • Configuration files excluded from version control.
  • Secret management services.
  • Runtime configuration systems.

The following example demonstrates how to use environment variables:

// SECURE: Reading secret from environment variable
public class PaymentProcessor
{
    private readonly string _stripeKey;
    
    public PaymentProcessor()
    {
        _stripeKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY") 
                     ?? throw new InvalidOperationException("STRIPE_API_KEY not configured");
    }
}

Use secret management services

Modern cloud platforms provide dedicated services for storing and accessing secrets securely.

Examples include:

  • Azure Key Vault: Centralized secret storage with access control and auditing.
  • AWS Secrets Manager: Automated rotation and fine-grained access policies.
  • HashiCorp Vault: Enterprise secret management with dynamic secrets.
  • GitHub Secrets: Encrypted secrets for GitHub Actions workflows.

These services provide:

  • Encryption at rest and in transit.
  • Access logging and auditing.
  • Fine-grained permission controls.
  • Automatic rotation capabilities.
  • Centralized secret management.

Rotate secrets regularly and immediately when exposed

Implement processes for rotating secrets on a schedule and immediately when exposure is suspected.

Regular rotation limits the window of opportunity if a secret is compromised. Immediate rotation when exposure is detected minimizes potential damage.

Use different secrets for different environments

Never use production credentials in development, testing, or staging environments.

Separate secrets by environment:

  • Development: Lower-privilege credentials, potentially test accounts.
  • Staging: Separate credentials that mirror production permissions.
  • Production: Highest security, monitored access, restricted permissions.

Separating secrets by environment ensures that compromised development credentials can't affect production systems.

Implement least privilege access

Configure secrets with only the minimum permissions required for their intended purpose.

For example:

  • Database credentials: Read-only access if writes aren't needed.
  • API keys: Restrict to specific endpoints or operations.
  • Cloud credentials: Limit to specific resources and actions.

If a secret is exposed, limited permissions reduce the potential damage.

Scan repositories for secrets

Use automated tools to detect secrets in code before they're committed or pushed to repositories.

For example, GitHub provides the following tools:

  • GitHub Secret Scanning: Automatically scans for known secret patterns.
  • GitHub Push Protection: Blocks any commits that contain secrets.

These tools act as safety nets, catching secrets that developers accidentally include in code.