Back to Blog
critical SEVERITY9 min read

How Hardcoded API Keys happen in JavaScript plugins and how to fix them

A critical hardcoded API key was discovered in `plugins/ocr.js` at line 21, where the OCR integration used a plaintext fallback credential `'K81241004488957'` whenever the `OCR_API_KEY` environment variable was absent. This exposed a live API key to anyone with repository access, enabling unauthorized use of the OCR service. The fix removes the hardcoded fallback entirely and fails fast with a clear error message when the required environment variable is not configured.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

This is a hardcoded secret vulnerability (CWE-798) in a JavaScript OCR plugin (`plugins/ocr.js`), where the API key `'K81241004488957'` was embedded as a fallback value in a `form.append()` call. Any attacker with source code access — through a public repo, leaked archive, or reverse engineering — could extract and abuse this key. The fix removes the fallback literal and adds an early-exit guard that returns an error message when `process.env.OCR_API_KEY` is not set, ensuring credentials are never baked into the codebase.

Vulnerability at a Glance

cweCWE-798
fixRemove the hardcoded fallback and add an environment variable presence check before the API call
riskUnauthorized access to the OCR API service; credential theft via source code exposure
languageJavaScript (Node.js)
root cause`form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')` embeds a live API key as a fallback literal
vulnerabilityHardcoded API Key / Hardcoded Secret

How Hardcoded API Keys Happen in JavaScript Plugins and How to Fix Them


The Vulnerability at a Glance

Field Detail
Vulnerability Hardcoded API Key / Hardcoded Secret
CWE CWE-798: Use of Hard-coded Credentials
Language JavaScript (Node.js)
Risk Unauthorized OCR API access; credential theft via source code
Root Cause form.append('apikey', process.env.OCR_API_KEY \|\| 'K81241004488957')
Fix Remove fallback literal; fail fast when env var is absent

Direct Answer

This is a hardcoded secret vulnerability (CWE-798) in plugins/ocr.js. The API key 'K81241004488957' was embedded as a fallback value in a form.append() call. Any attacker with source code access could extract and abuse this key. The fix removes the fallback and adds an early-exit guard: if process.env.OCR_API_KEY is not set, the function replies with an error message and stops execution before the API call is made.


Introduction

The plugins/ocr.js file handles optical character recognition requests — a user sends an image, the plugin downloads it, submits it to an external OCR API, and returns the extracted text. It's a convenient feature, but a single line of convenience-oriented code turned it into a critical security liability:

form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')

The || operator here is doing what JavaScript developers use it for all the time: providing a default value when the left side is falsy. In most contexts, that's perfectly reasonable. In the context of API credentials, it means a live, functional API key — K81241004488957 — is sitting in plaintext inside the source file, ready to be discovered by anyone who can read the code.

This pattern is so common it has its own CWE entry: CWE-798, Use of Hard-coded Credentials. It's also one of the most consistently exploited categories in real-world breaches, precisely because it feels harmless when you write it.


The Vulnerability Explained

What Actually Happened

At line 21 of plugins/ocr.js, the OCR plugin constructs a multipart form submission to an external OCR API service. The apikey field is populated using this expression:

// VULNERABLE — line 21 of plugins/ocr.js
form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')

The intent was likely: "Use the environment variable in production, but have a fallback so the feature works during development or if someone forgets to set the variable." That's a reasonable developer concern. The execution, however, is dangerous.

The fallback 'K81241004488957' is not a placeholder. It is a real API key that was almost certainly used during development or testing. Once committed to the repository, it becomes permanently accessible in the git history, even if the line is later changed.

How This Gets Exploited

The attack chain here is short — rated as a 2-step chain in the vulnerability assessment:

  1. Step 1 — Source acquisition: An attacker gains access to the source code. This could happen through a public GitHub repository, a leaked backup, a compromised developer machine, or even by decompiling a distributed build of the application.

  2. Step 2 — Key extraction and abuse: The attacker searches for the string apikey or OCR_API_KEY in the codebase, finds 'K81241004488957' at plugins/ocr.js:21, and begins making OCR API requests authenticated with that key.

No network interception, no brute force, no sophisticated tooling required. The key is in the code.

Real-World Impact

Depending on the OCR API provider's pricing and rate limits, an attacker exploiting this key could:

  • Run up significant charges on the account associated with the key, resulting in unexpected billing for the application owner
  • Exhaust API quotas, causing the legitimate OCR feature to fail for real users
  • Access usage data or account information if the API key grants broader permissions than just OCR submissions
  • Use the key as a pivot — some API providers expose account management endpoints authenticated by the same key

Because this is described as a web service in the threat model, the source code exposure surface is elevated. The application is deployed and potentially accessible to a wide range of actors who might attempt to obtain its code.


The Fix

The fix makes two precise changes to plugins/ocr.js:

Before

const form = new FormData()
form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')
form.append('language', 'eng')

After

if (!process.env.OCR_API_KEY) return m.reply('OCR API key is not configured.')
const form = new FormData()
form.append('apikey', process.env.OCR_API_KEY)
form.append('language', 'eng')

Why This Fix Works

Change 1 — Early exit guard: The added line if (!process.env.OCR_API_KEY) return m.reply('OCR API key is not configured.') checks for the environment variable before any form construction or API call happens. If the variable is absent, the function returns immediately with a user-facing error message. This is a "fail fast" pattern — the application makes its configuration requirements explicit rather than silently falling back to a hardcoded value.

Change 2 — Removal of the fallback literal: The || 'K81241004488957' expression is completely removed. form.append('apikey', process.env.OCR_API_KEY) now passes the environment variable directly. Since the guard above ensures OCR_API_KEY is truthy before this line executes, there is no risk of submitting an undefined or empty apikey to the OCR service.

The fix is scoped to one file and one logical path — the OCR request handler. Valid inputs (a properly configured OCR_API_KEY environment variable) are completely unaffected. Only the insecure fallback behavior is removed.

What Happens to the Hardcoded Key?

The key K81241004488957 should be treated as permanently compromised. Even after this fix is merged, the key exists in the repository's git history. The correct remediation steps alongside this code fix are:

  1. Revoke the key immediately via the OCR API provider's dashboard
  2. Generate a new key and store it in the deployment environment's secret management system
  3. Audit API usage logs for the old key to determine if unauthorized access occurred
  4. Consider a git filter-repo or BFG Repo Cleaner run if the repository is public, to purge the key from history

Prevention & Best Practices

Never Use || for Credential Fallbacks

The pattern process.env.SECRET || 'hardcoded_value' is one of the most common sources of hardcoded credential bugs in Node.js applications. The || operator is idiomatic JavaScript for default values, which is exactly why it's dangerous here — it looks completely normal.

Instead, use an explicit check:

// Good: fail fast with a clear error
if (!process.env.OCR_API_KEY) {
  throw new Error('OCR_API_KEY environment variable is required but not set.')
}

Or use a configuration validation library at startup:

// Using a schema validator like envalid or zod
const env = cleanEnv(process.env, {
  OCR_API_KEY: str({ docs: 'https://ocr.space/ocrapi' }),
})

Validate All Required Secrets at Startup

Don't wait until a feature is invoked to discover that a required secret is missing. Validate all required environment variables when the application starts, so misconfiguration fails loudly at deploy time rather than silently at runtime (or worse, silently succeeds with a hardcoded fallback).

Use a Secrets Manager for Production

For production deployments, avoid environment variables stored in .env files on disk. Use a dedicated secrets management solution:

  • AWS Secrets Manager or AWS Parameter Store
  • HashiCorp Vault
  • Azure Key Vault / GCP Secret Manager
  • Doppler or Infisical for developer-friendly workflows

Add Secret Scanning to Your CI/CD Pipeline

Tools that can catch hardcoded secrets before they reach production:

  • TruffleHog — scans git history for high-entropy strings and known secret patterns
  • GitLeaks — configurable secret detection for git repos
  • GitHub Secret Scanning — built into GitHub, alerts on known API key formats
  • Semgrep — static analysis rules for hardcoded credentials

Relevant Security Standards

  • OWASP Top 10 A02:2021 — Cryptographic Failures: Storing or transmitting credentials without protection
  • OWASP Secrets Management Cheat Sheet: Guidance on handling secrets in applications
  • CWE-798: Use of Hard-coded Credentials
  • CWE-259: Use of Hard-coded Password (related, more specific)

Key Takeaways

  • The || 'fallback' pattern is dangerous for secrets: In plugins/ocr.js, process.env.OCR_API_KEY || 'K81241004488957' silently used a live API key whenever the environment variable was absent — a pattern that looks harmless but creates a critical exposure.
  • Hardcoded keys survive code changes via git history: Even after the fix is merged, K81241004488957 exists in the repository's commit history and must be revoked at the provider level.
  • Fail fast beats silent fallback for credentials: The replacement guard if (!process.env.OCR_API_KEY) return m.reply('OCR API key is not configured.') makes misconfiguration visible and actionable instead of silently dangerous.
  • Source code access is a realistic threat vector: This was assessed as a 2-step exploit chain — obtain source, extract key. For web services with any public-facing exposure, this attack surface is real.
  • Secret scanning in CI would have caught this before merge: Tools like TruffleHog or GitHub Secret Scanning can detect patterns like K81241004488957 automatically, preventing this class of bug from reaching production.

How Orbis AppSec Detected This

  • Source: The hardcoded string literal 'K81241004488957' embedded directly in plugins/ocr.js at line 21
  • Sink: form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957') — the credential is passed to an external HTTP request to the OCR API service
  • Missing control: No environment variable presence validation; the || fallback operator bypassed any requirement to configure the secret externally
  • CWE: CWE-798 — Use of Hard-coded Credentials
  • Fix: Removed the || 'K81241004488957' fallback and added an explicit early-exit check that returns an error message when OCR_API_KEY is not set

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The vulnerability in plugins/ocr.js is a textbook example of how developer convenience — a simple || fallback to keep the OCR feature working without configuration — can create a critical security exposure. The key K81241004488957 was never meant to be permanent, but once committed, it became a permanent part of the repository's history and a permanent risk.

The fix is minimal and precise: one line added, one literal removed. But the lesson is broader. Every time you reach for process.env.SECRET || 'some_value' in a Node.js application, ask whether that fallback value is something you'd be comfortable publishing publicly. If the answer is no, use a fail-fast guard instead.

Credentials belong in secrets managers and environment variables — not in source code, not in comments, and not in || fallback expressions.


References

Frequently Asked Questions

What is a hardcoded API key vulnerability?

A hardcoded API key is a secret credential embedded directly in source code as a string literal. Anyone who can read the code — including via public repositories, leaked archives, or decompiled binaries — can extract and misuse the key.

How do you prevent hardcoded secrets in JavaScript?

Always load sensitive credentials from environment variables or a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault). Never use the `|| 'fallback_secret'` pattern in production code, and add a startup check that fails fast if required variables are missing.

What CWE is hardcoded API key?

Hardcoded credentials are classified under CWE-798: Use of Hard-coded Credentials, with the related CWE-259 covering hard-coded passwords specifically.

Is rotating the API key enough to prevent this vulnerability?

Rotation helps remediate the immediate exposure, but it is not sufficient on its own. The hardcoded literal must be removed from the codebase, or a future developer could unknowingly re-deploy the old key. The root cause — the fallback pattern — must be fixed in code.

Can static analysis detect hardcoded API keys?

Yes. Tools like Semgrep, TruffleHog, GitLeaks, and GitHub's secret scanning can detect string literals that match API key patterns. Orbis AppSec's multi-agent AI scanner flagged this exact pattern in `plugins/ocr.js`.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #74

Related Articles

high

How Information Disclosure happens in Go dependency management and how to fix it

CVE-2026-42151 is a high-severity information disclosure vulnerability in the Prometheus monitoring library (github.com/prometheus/prometheus) that exposed Azure OAuth client secrets through the Prometheus configuration API endpoint. Applications depending on versions prior to v0.311.3 were at risk of leaking sensitive Azure credentials to anyone with access to the config API. The fix involves upgrading the dependency in go.mod from v0.310.0 to v0.311.3.

critical

How Hardcoded Credentials Happen in Node.js Express Routes and How to Fix Them

A critical hardcoded credential vulnerability was discovered in `routes/bing-routes.js` where a WordPress application password was embedded directly in the source code as a fallback value. This meant anyone with access to the repository could obtain valid authentication credentials. The fix removes the hardcoded fallback and requires proper environment variable configuration.

medium

How Hardcoded AWS Credentials Happen in Node.js Configuration Files and How to Fix It

A critical security issue was discovered in the S3 Express deployment configuration file where an AWS Secret Access Key was hardcoded as a placeholder example. This vulnerability could allow attackers to gain unauthorized access to AWS resources if the example file was accidentally deployed to production or committed to version control without proper sanitization.

critical

How API key exposure in client-side HTML happens in JavaScript web applications and how to fix it

A critical security vulnerability was discovered in all.html where a Yandex Maps API key was embedded directly in client-side HTML at line 68. This pattern exposed API credentials to anyone viewing the page source, enabling unlimited unauthorized API requests. The fix removed the API key from the client-side code, demonstrating proper API key management for JavaScript applications.

critical

How Missing Rate Limiting happens in Express.js and how to fix it

Two public API endpoints in `server.js` — `/api/health` and `/api/contact` — were exposed without any rate limiting middleware, allowing attackers to exhaust server resources or spam an SMTP server with unlimited requests. The fix adds rate limiting to both endpoints, with stricter controls on the resource-intensive `/api/contact` route that triggers email sending operations. This change closes a directly exploitable denial-of-service vector in a production web service.

critical

How Wildcard postMessage Origins Happen in Chrome Extensions and How to Fix Them

A critical cross-origin message injection vulnerability was discovered in `offscreen.js`, where a wildcard `"*"` origin in `postMessage` calls and a missing source validation check allowed any webpage to send arbitrary messages to the extension's iframe. The fix adds an explicit source check and replaces the wildcard with `"null"` to restrict communication to the trusted iframe only. This change prevents malicious websites from hijacking the extension's offscreen message channel.