Back to Blog
critical SEVERITY8 min read

How Plaintext Secret Storage Happens in Cloudflare Workers (wrangler.toml) and How to Fix It

A critical misconfiguration in `platforms/m365/wrangler.toml` left developers one copy-paste away from committing live API keys directly into git history. The fix adds an explicit warning comment blocking the `[vars]` anti-pattern and adds `.dev.vars` to `.gitignore`, ensuring secrets flow through Cloudflare's encrypted `wrangler secret` mechanism instead of plaintext config. This matters because git history is permanent — a key committed even once can be extracted long after it's "deleted."

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 26, 2026•Reviewed August 26, 2026

Answer Summary

This vulnerability is a hardcoded-secrets risk (CWE-312: Cleartext Storage of Sensitive Information) in a Cloudflare Workers configuration file (`platforms/m365/wrangler.toml`). The `[vars]` section of `wrangler.toml` is committed to git in plaintext, meaning any API key placed there — such as `ANTHROPIC_API_KEY` or `API_TOKEN` — becomes permanently readable in version history. The fix adds a prominent `WARNING` comment to block the `[vars]` anti-pattern, redirects developers to `wrangler secret put`, and adds `.dev.vars` to `.gitignore` to prevent local dev secrets from being committed.

Vulnerability at a Glance

cweCWE-312
fixAdded explicit WARNING comment blocking [vars] usage for secrets and added .dev.vars to .gitignore
riskLive API keys committed to git history, readable by anyone with repo access
languageTOML / Cloudflare Workers (JavaScript/TypeScript)
root causewrangler.toml [vars] section is version-controlled plaintext with no enforcement against storing secrets there
vulnerabilityCleartext Storage of Sensitive Information (API Key in Config File)

The Problem With "Just Don't Put Keys Here"

Documentation comments are not security controls.

In the platforms/m365/wrangler.toml file for this Cloudflare Workers deployment, a comment instructed developers to set ANTHROPIC_API_KEY and API_TOKEN via wrangler secret put. That's correct advice. But the comment sat directly adjacent to the [vars] section — the exact place where a developer under deadline pressure might paste a key "just to test something" and accidentally commit it.

That's the vulnerability: not a missing encryption library, not a broken authentication flow, but a configuration file that was one distracted commit away from leaking production credentials into version control forever.


The Vulnerability Explained

What Makes [vars] Dangerous for Secrets

Cloudflare Workers has two ways to inject values into a Worker's environment:

  1. [vars] in wrangler.toml — plaintext key-value pairs, committed to git alongside your code
  2. wrangler secret put — encrypted secrets stored in Cloudflare's secret store, never written to disk or version control

The distinction is critical. Anything in [vars] is:
- Committed to git in plaintext
- Visible in every clone, fork, and CI/CD log that checks out the repo
- Permanent in git history, even if later deleted

Here's what the vulnerable section looked like before the fix:

# platforms/m365/wrangler.toml (before fix, line 7-11)

ALLOWED_ORIGINS = ""

# Set secrets via:
#   wrangler secret put ANTHROPIC_API_KEY
#   wrangler secret put API_TOKEN

The problem isn't what's written — it's what's missing. There's no barrier between the [vars] block above and the comment below. A developer who sees ALLOWED_ORIGINS = "" and needs to add ANTHROPIC_API_KEY for local testing might naturally write:

# DANGEROUS — do not do this
[vars]
ALLOWED_ORIGINS = ""
ANTHROPIC_API_KEY = "sk-ant-api03-..."   # ← now in git forever
API_TOKEN = "Bearer eyJ..."              # ← same

Once that commit is pushed — even to a private repository — the key is in the git object database. Deleting the line in a follow-up commit does not remove it from history.

The Attack Scenario

The PR's exploitation scenario is concrete: an attacker gains read access to the repository via leaked Git credentials, an exposed CI/CD log, or a compromised developer account. With that access, they run:

git log --all --full-history -- platforms/m365/wrangler.toml
git show <commit-hash>:platforms/m365/wrangler.toml

If an ANTHROPIC_API_KEY was ever committed to [vars], it appears in plain text. The attacker now has:
- Full access to the Anthropic API under the victim's billing account
- Potentially unbounded LLM API spend
- Access to any conversation history or fine-tuning data tied to that key

The API_TOKEN exposure is similarly severe — depending on what it authenticates, it could grant access to M365 data, admin endpoints, or downstream services.

Why the 503 Fallback Doesn't Help

The codebase includes a defensive control: when secrets are missing at runtime, the Worker returns a 503 error rather than proceeding without credentials. This is good practice — but it protects against absent secrets, not exposed ones. If a key is committed to git, it isn't missing. The Worker runs fine. The attacker also runs fine, using the same key from outside the Worker entirely.


The Fix

The fix is two targeted changes that together close the gap between documentation intent and developer behavior.

Change 1: Explicit Warning in wrangler.toml

# Before
# Set secrets via:
#   wrangler secret put ANTHROPIC_API_KEY
#   wrangler secret put API_TOKEN

# After
# WARNING: NEVER add ANTHROPIC_API_KEY or API_TOKEN to [vars] above.
# [vars] is committed to git and is NOT encrypted. Use wrangler secrets:
#   wrangler secret put ANTHROPIC_API_KEY
#   wrangler secret put API_TOKEN

The change is minimal in lines but significant in intent. The original comment read like a suggestion. The new comment reads like a guardrail. By naming the specific variables (ANTHROPIC_API_KEY, API_TOKEN) and explaining why [vars] is unsafe ("committed to git and is NOT encrypted"), the warning gives a developer the information they need at the exact moment they might make the mistake.

This is the principle of contextual security guidance — security instructions are most effective when they appear at the point of decision, not in a README three directories away.

Change 2: .dev.vars Added to .gitignore

# .gitignore

+# Cloudflare Workers local dev secrets — must never be committed
+.dev.vars
+
 # Internal lead documents — not for public consumption

Cloudflare Workers supports a .dev.vars file for local development — it's the local equivalent of wrangler secret put, letting developers set secret values for wrangler dev without touching wrangler.toml. But .dev.vars is only safe if it's gitignored. Without this entry, a developer creating .dev.vars for local testing could accidentally commit it.

The comment in the .gitignore entry matters too: "must never be committed" explains the why, making it less likely a future developer will remove the entry thinking it's unnecessary.

What This Fix Does NOT Do (And Why That's Okay)

This fix doesn't add runtime enforcement — it doesn't fail the build if [vars] contains a secret-shaped value. That would be a stronger control, but it's also a more invasive change. The current fix correctly scopes the change to the minimum necessary: clear guidance at the point of risk, and a gitignore entry that closes the .dev.vars vector. The PR notes that "the project's existing tests still pass, so intended behavior is unchanged."


Key Takeaways

  • wrangler.toml [vars] is git-committed plaintext — treat it like a public file and never store ANTHROPIC_API_KEY, API_TOKEN, or any credential there, regardless of how temporary the intent is.
  • A comment saying "don't do X" is not a control — the original comment correctly pointed to wrangler secret put, but without a warning explaining why, it provided no friction against the dangerous alternative.
  • .dev.vars needs to be gitignored explicitly — Cloudflare's local dev secrets file is only safe if it never reaches version control; the absence of this gitignore entry was a second vector for the same class of exposure.
  • Git history is permanent — rotating a key after committing it is mandatory, not optional. Deleting the line in a follow-up commit does not remove it from git log.
  • Contextual warnings outperform distant documentation — the fix's WARNING: NEVER add ANTHROPIC_API_KEY or API_TOKEN to [vars] comment is more effective than any README section because it appears at the exact point of risk.

How Orbis AppSec Detected This

  • Source: The [vars] section of platforms/m365/wrangler.toml at line 11, where environment variables are defined in plaintext and committed to version control
  • Sink: Any git clone, CI/CD checkout, or repository browser that reads wrangler.toml — the "dangerous call site" is the git commit itself, which permanently stores the value
  • Missing control: No explicit prohibition against placing ANTHROPIC_API_KEY or API_TOKEN in [vars]; no .dev.vars gitignore entry to block the local dev secret file from being committed
  • CWE: CWE-312 — Cleartext Storage of Sensitive Information
  • Fix: Added a WARNING comment naming the specific forbidden variables and explaining that [vars] is git-committed plaintext, plus added .dev.vars to .gitignore

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 platforms/m365/wrangler.toml is a reminder that security misconfiguration doesn't require a sophisticated exploit — sometimes it just requires a developer who doesn't know which config section is safe for secrets. The fix is small: two files, a warning comment, and a gitignore entry. But the impact is significant: it closes the gap between "we told developers to use wrangler secrets" and "we made it hard for developers to accidentally do the wrong thing."

Cloudflare Workers makes secret management easy with wrangler secret put. The failure mode here wasn't a missing feature — it was a missing guardrail. The lesson for any project using configuration files: document the safe path, warn against the unsafe path, and use tooling to enforce the difference.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #11

Related Articles

critical

redmine_drawio View Hook Inlines Base64 Redmine API Keys

The Redmine drawio plugin's body-bottom view listener embedded the logged-in user's Redmine REST API key into client-side JavaScript on every wiki and issue page, "protected" only by Base64 encoding of the reversed string. Any script on the page — or anyone with browser developer tools, a cached copy of the HTML, or a proxy log — could decode it in one line and act as that user through the Redmine REST API. The fix removes the embedded credential from the rendered page entirely; the plugin's dia

critical

Yandex Translate API Key Leaked via URL Query Parameter

The `translateYandex()` helper built its request URL by interpolating the caller-supplied API key directly into the query string, meaning every call leaked the credential into server access logs, proxy logs, and any Referer header sent by intermediaries. The fix switches the request from a GET with the key in the URL to a POST with the key in the request body via `URLSearchParams`, removing the credential from any URL-logging surface entirely.

critical

Google Generative AI Keys Exposed in Client-Side Fetch URLs

A client-side AI model discovery utility was embedding Google Generative AI API keys directly in fetch request URLs, making them visible to any user inspecting network traffic or browser DevTools. The fix moves the key from the URL query parameter to a secure HTTP header, eliminating exposure while maintaining API authentication.

critical

How credential leakage through console logging happens in JavaScript browser extensions and how to fix it

A browser extension's `src/background/credentials.js` printed the full Strava authentication cookie string — including a signed JWT and CloudFront-Signature values — straight into the extension console via `console.debug`. Anyone who could open DevTools on the background page (or any tooling that scraped the console) could copy a live session and impersonate the user. The fix replaces the credential payload in both log statements with `Boolean(credentials)` and strips a realistic-looking JWT out

critical

How Hardcoded Secrets Compromise Authentication in JavaScript and How to Fix It

A critical vulnerability in `Tool/QuantumultX/Rewrite/RRSP.js` exposed hardcoded API authentication credentials—a TOKEN and UMID device identifier—directly in source code. Anyone with repository access could extract these credentials to impersonate the legitimate user and gain full account access to the RRTV API service. The fix replaced hardcoded secrets with empty placeholders, forcing users to manually configure credentials through secure channels.

critical

How API Key Exposure in Request Bodies happens in React and how to fix it

The Chatbot component in gitforme was transmitting Azure OpenAI API keys inside JSON request bodies, causing them to be logged by servers, proxies, and middleware. By moving the apiKey from requestBody.apiKey to an Authorization header, credentials are now protected from persistence in generic request logging infrastructure.