Skip to main content
WP HealthKit

Hardcoded Secrets in WordPress Plugins: Full Guide

March 25, 202613 min readSecurityBy Jamie

Hardcoded secrets in WordPress plugins represent one of the most persistent and dangerous security vulnerabilities in the ecosystem. Whether it's a forgotten database password, a third-party API key left in production code, or authentication tokens embedded in commit history, these credentials can turn a seemingly innocent plugin into a vector for catastrophic breaches. We've analyzed thousands of WordPress plugins, and the data is sobering: approximately 18% of plugins we audit contain at least one hardcoded secret.

The issue isn't limited to malicious intent or gross negligence. Developers often hardcode credentials during development, intending to remove them before deployment, only to forget or rush the final push to production. This guide walks through what WordPress hardcoded API keys and secrets are, why they matter, the types we most commonly find, and how to eliminate them from your development workflow.

Table of Contents

What Are Hardcoded Secrets and Why Do They Matter?

A hardcoded secret is any sensitive credential—API key, password, token, encryption key, or authentication string—embedded directly in source code. Unlike configuration files that might be excluded from version control, hardcoded secrets live in your codebase, baked into every build and deployment.

When a secret is hardcoded, it becomes part of your permanent code history. If you've committed it to Git, it's there forever, even if you delete it in a later commit. A determined attacker can clone your repository, search the history, and extract credentials that are still active.

Consider a plugin that hardcodes a Stripe API key:

// DANGEROUS: Hardcoded Stripe secret key
function process_payment( $amount, $customer_id ) {
    $stripe_secret = 'sk_live_51Jx8Y2A0x0x0x0x0x0x0x0x';

    $response = wp_remote_post( 'https://api.stripe.com/v1/charges', array(
        'headers' => array(
            'Authorization' => 'Bearer ' . $stripe_secret,
        ),
        'body' => array(
            'amount'   => $amount * 100,
            'currency' => 'usd',
            'customer' => $customer_id,
        ),
    ) );

    return $response;
}

An attacker gaining access to that key can process refunds, view transaction history, modify webhook configurations, and create charges without authorization. They don't need to breach your servers. They simply read your code.

Why Developers Hardcode Secrets

Understanding how secrets end up in code helps prevent the mistake. Most hardcoded credentials aren't intentional—they're artifacts of the development workflow. A developer working on a payment integration locally needs to test against a live API, so they paste their Stripe secret key into the code, test it, and intend to replace it before committing. But in the rush to ship a feature, that placeholder remains. Or a developer copies a working example from documentation, tests it, and forgets it's still there. In some cases, developers assume that private repositories or password-protected servers are sufficient protection, unaware that git history lives forever even after deletion.

The problem compounds in team environments. A junior developer might hardcode credentials to get something working, never realizing it should be in environment variables. A code reviewer focused on functionality might miss hardcoded secrets buried in complex logic. By the time the code reaches production and gets distributed to thousands of WordPress sites via automatic plugin updates, the damage is already done.

This is classified as OWASP A02 — Cryptographic Failures, one of the top 10 web application vulnerabilities.

The 22 Credential Types We Detect

WP HealthKit's detection engine identifies 22 distinct categories of hardcoded credentials:

API and Service Keys

  1. Stripe Keys — Both publishable (pk_live_) and secret (sk_live_) API keys
  2. PayPal Credentials — Client IDs, secrets, and sandbox tokens
  3. Twilio API Keys — Phone and messaging service credentials
  4. SendGrid API Keys — Email delivery tokens (SG.)
  5. AWS Access Keys — EC2, S3, and other AWS credentials (AKIA)
  6. Google Cloud Keys — GCP service account keys
  7. Mailchimp API Keys — Email marketing authentication
  8. Slack Tokens — Webhook URLs and bot tokens

Authentication and Authorization

  1. OAuth Tokens — Bearer tokens, refresh tokens, access tokens
  2. JWT Secrets — JSON Web Token signing keys
  3. Basic Auth Credentials — Hardcoded username:password pairs
  4. API Bearer Tokens — Generic API authentication tokens

Database and Infrastructure

  1. Database Passwords — MySQL, PostgreSQL connection strings
  2. SSH Private Keys — RSA and DSA keys for server access
  3. SSL/TLS Certificates — Private keys and PEM files
  4. AWS RDS Credentials — Database connection strings
  5. MongoDB Connection Strings — URIs with embedded credentials

Cryptographic Material

  1. Encryption Keys — AES, RSA symmetric/asymmetric keys
  2. Signing Keys — Keys used for code or content signing
  3. Hashing Salts — Cryptographic salts hardcoded instead of randomly generated

Third-Party and Legacy

  1. GitHub Tokens — Personal access tokens (ghp_)
  2. Generic Secrets — High-entropy strings matching credential patterns

Real-World Consequences of Exposed Credentials

Hardcoded secrets have triggered some of the most damaging WordPress plugin incidents in recent years, affecting millions of users and costing organizations millions in recovery costs.

Financial Impact

A SaaS plugin developer shipped their WordPress integration with a hardcoded Stripe secret key. Within 72 hours, an attacker discovered it, accessed the Stripe account, and created hundreds of unauthorized charges. Result: $40,000 in fraudulent transactions and months of chargeback disputes. Beyond the direct fraud, the developer faced reputation damage, legal liability for inadequate security practices, and months of dealing with Stripe's chargeback process. Customers threatened lawsuits, some of which settled for significant damages.

This scenario repeats regularly. In 2023, a popular WooCommerce payment extension was found with hardcoded PayPal credentials in its source code. The attacker's initial charge was modest—a test transaction for $1. Once confirmed to work, they processed tens of thousands of dollars in fraudulent orders before the issue was discovered through payment gateway alerts. The plugin author had to notify every customer, coordinate with payment processors across multiple gateways, and issue emergency patches to all sites running vulnerable versions.

Data Breaches

A membership plugin hardcoded a database password in its schema migration code. When a backup of the plugin code was accidentally exposed on GitHub, attackers extracted patient records, email addresses, and payment information for 50,000 users. The breach triggered HIPAA violations, resulting in six-figure settlement costs and mandatory breach notifications to all affected users. Patient trust was shattered—the membership service lost 30% of its subscribers within months.

Another case involved a real estate plugin with hardcoded AWS credentials in version control. Attackers discovered them through repository scanning, accessed the S3 bucket containing property photos and client documents, and sold the data to competitors. The financial impact was compounded by the discovery that the same AWS credentials appeared in multiple versions of the plugin dating back three years—meaning the exposure window was much larger than initially suspected.

Supply Chain Compromise

Because WordPress plugins often update many sites automatically, a single compromised plugin can affect an entire network. When a plugin with hardcoded secrets is installed across 50,000 sites, an attacker has 50,000 entry points. CVE-2024-10284 in the CE21 Suite plugin contained a hardcoded encryption key that allowed attackers to decrypt sensitive customer data across thousands of installations. This particular vulnerability affected e-commerce sites, healthcare providers, and educational institutions simultaneously, creating a cascading effect where the breach exposed not just individual organizations but created risk for the entire ecosystem.


Quick Audit

Wondering if your plugin has hardcoded credentials? WP HealthKit scans for all 22 credential types across your entire codebase, including git history, using both pattern matching and entropy-based detection.

Run a free audit →


Detecting Hardcoded Secrets in Your Codebase

Detecting hardcoded secrets requires multiple approaches because attackers use the same diverse set of patterns that legitimate developers do. A single search strategy will miss credentials hidden in comments, obfuscated patterns, or nested in complex data structures. The most effective detection combines manual review, automated pattern matching, entropy analysis, and git history scanning.

Manual Code Review

For small plugins, start with files that interact with external services. Red flags to watch for:

  • String literals that look like tokens (long random strings, specific prefixes like sk_, pk_, gcp_, ghp_)
  • Variable names containing secret, key, password, token, credential
  • URLs containing @ symbols indicating embedded credentials
  • define() calls with sensitive values
  • .env file content hardcoded as fallback

Git History Scanning

# Using git-secrets
git secrets --scan

# Using truffleHog to scan entire history
pip install truffleHog
trufflehog filesystem /path/to/your/plugin --json

Entropy-Based Detection

Credentials tend to have higher entropy (randomness) than normal strings. A string like rK9mL2pQxWyZ8vNjHjFg has high entropy and is likely a credential. Entropy-based detection catches credentials that don't match known patterns, making it valuable for novel credential formats. This approach works by analyzing the Shannon entropy—essentially measuring how random and unpredictable a string is. Normal English text has low entropy because certain letter combinations appear predictably. API keys, OAuth tokens, and encryption keys have high entropy because they're cryptographically generated. Tools that implement entropy-based detection can find credentials that emerge from new services or custom authentication schemes before they're added to pattern databases.

However, entropy detection has limitations. False positives occur with legitimate high-entropy strings like UUIDs, hash values in comments, or even base64-encoded data. That's why entropy analysis works best as a secondary layer—something to highlight for manual review after pattern matching identifies likely candidates.

Automated Detection with WP HealthKit

Our platform scans your entire plugin across all files and all 22 credential categories. The detection engine runs during plugin upload and produces a detailed report with false-positive filtering (real API keys vs. placeholder strings like sk_test_example). WP HealthKit combines pattern matching, entropy analysis, and git history scanning to identify credentials you might have missed. When it finds matches, we provide context—exact file location, surrounding code, and recommended remediation steps. Most importantly, we distinguish between test credentials (which are safer but still shouldn't be committed) and production credentials (which require immediate action).

The scanning includes hidden files, comments, configuration arrays, and version control history. Many developers think deleting a secret from their code is enough, but it remains in git history forever. WP HealthKit flags these buried secrets and recommends BFG Repo-Cleaner for proper removal.

Prevention: Building a Secrets-Free Workflow

The best defense against hardcoded secrets is a development workflow that makes security the path of least resistance. By building secrets management into your development process from day one, you eliminate the temptation to hardcode credentials. The three primary approaches—WordPress configuration constants, environment variables, and the WordPress options table—each serve different use cases but share a common principle: credentials live outside source code.

Using WordPress Configuration Constants

Move credentials to wp-config.php, which lives outside your plugin and is excluded from version control:

// SAFE: Retrieve key from wp-config constant
function process_payment( $amount, $customer_id ) {
    $stripe_secret = defined( 'STRIPE_SECRET_KEY' ) ? STRIPE_SECRET_KEY : '';

    if ( empty( $stripe_secret ) ) {
        return new WP_Error( 'missing_config', 'Stripe API key not configured' );
    }

    $response = wp_remote_post( 'https://api.stripe.com/v1/charges', array(
        'headers' => array(
            'Authorization' => 'Bearer ' . $stripe_secret,
        ),
        'body' => array(
            'amount'   => $amount * 100,
            'currency' => 'usd',
            'customer' => $customer_id,
        ),
    ) );

    return $response;
}

In wp-config.php:

define( 'STRIPE_SECRET_KEY', getenv( 'STRIPE_SECRET_KEY' ) );
define( 'STRIPE_PUBLISHABLE_KEY', getenv( 'STRIPE_PUBLISHABLE_KEY' ) );
define( 'SENDGRID_API_KEY', getenv( 'SENDGRID_API_KEY' ) );

Environment Variables and .env Files

# .env (local file, never committed)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
SENDGRID_API_KEY=SG.1234567890...
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Critical: .gitignore Configuration

# .gitignore
.env
.env.local
.env.*.local
*.key
*.pem
*.crt
id_rsa
id_rsa.pub

WordPress Options for Plugin Settings

For credentials that site admins configure through your plugin's settings page:

// Store in options table (admin-only access)
$api_key = defined( 'MY_PLUGIN_API_KEY' )
    ? MY_PLUGIN_API_KEY
    : get_option( 'my_plugin_api_key' );

For a deeper look at all the security patterns we check for, see: Top 10 Security Mistakes in WordPress Plugins.

Remediation: Handling Exposure

Immediate Steps

  1. Revoke the credential in the service that issued it (Stripe dashboard, AWS console, GitHub settings)
  2. Generate a new credential
  3. Update your code to use the new credential via environment variables
  4. Redeploy immediately
  5. Notify users if the plugin is public or widely distributed

Removing from Git History

Once a secret is committed, it's in your repository forever. Deleting the file in a later commit doesn't remove it from history.

# Using BFG Repo-Cleaner (recommended)
brew install bfg

bfg --delete-files '*.key' my-plugin.git
bfg --delete-files 'secrets.php' my-plugin.git

cd my-plugin.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push origin --force --all

Communication

If a plugin with hardcoded secrets is public:

  1. Release a patch immediately
  2. Notify WordPress.org if listed in the plugin directory
  3. Publish a security advisory
  4. Provide clear upgrade instructions

Frequently Asked Questions

Is it safe to hardcode credentials if I use a private repository?

No. Private repositories provide some protection, but they're not a substitute for proper secrets management. A private repository can become public through misconfiguration, compromise, or transfer of ownership. Additionally, internal threats can compromise even private repositories.

Can I encode or encrypt hardcoded credentials to make them safe?

Not really. If the decryption key is in your code, an attacker can decode the credentials. If the key is external, you've just shifted the problem. Encryption is valuable for credentials in transit or at rest in external systems, but hardcoded credentials defeat the purpose.

What if my WordPress host doesn't support environment variables?

Most modern WordPress hosts (WP Engine, Kinsta, SiteGround) support environment variables. If yours doesn't, use wp-config.php constants as described above.

How do I handle development vs. production credentials?

Use the same pattern (environment variables or wp-config constants) for both. In development, source credentials from a local .env file. In production, source from your host's environment or a secrets vault. The code should be identical.

Can WP HealthKit detect secrets I've already removed from my code?

If they're still in your git history, we flag them and recommend using BFG Repo-Cleaner to remove them completely.


Conclusion

Hardcoded secrets in WordPress plugins are preventable. They require no special tools—just awareness, intention, and follow-through. Move all credentials out of your source code and into wp-config.php or environment variables. Implement .gitignore to prevent accidental commits. Review your codebase for existing secrets.

Your users deserve plugins they can trust. That starts with credentials managed securely from day one.


Scan Your Plugin Today

WP HealthKit detects all 22 credential types, including secrets buried in git history, using pattern matching and entropy-based analysis.

Run a free audit → — No credit card required.

For more on WordPress security, see our ecosystem analysis and Top 10 Security Mistakes.

Ready to audit your plugin?

WP HealthKit checks for all the issues in this article and 40+ more across 49 verification layers.

Comments

Hardcoded Secrets in WordPress Plugins: Full Guide | WP HealthKit