Back to Blog
critical SEVERITY6 min read

How Hardcoded Cryptographic Keys in JavaScript Proxy Scripts Get Exposed and How to Fix Them

A critical vulnerability was discovered in `ghs/91Pornad.js` where AES encryption keys, initialization vectors, and HMAC signing salts were stored as plaintext string constants in a publicly distributed proxy script. Since these scripts are fetched from GitHub raw URLs by Quantumult X and Surge users, anyone could extract the cryptographic credentials and forge API requests or decrypt responses. The fix applies base64 encoding via `atob()` to obfuscate the sensitive values at rest.

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

Answer Summary

This vulnerability (CWE-798: Use of Hard-coded Credentials) involves hardcoded AES keys, IVs, and signing salts stored as plaintext string literals in a JavaScript proxy script (`ghs/91Pornad.js`) distributed publicly via GitHub. The fix replaces the plaintext constants with `atob()`-decoded base64 strings to prevent casual extraction of cryptographic material from source code inspection.

Vulnerability at a Glance

cweCWE-798
fixEncode secrets with base64 and decode at runtime using atob() to prevent trivial extraction
riskAttackers can extract AES keys and signing salts to decrypt API responses or forge authenticated requests
languageJavaScript
root causeAES_KEY, AES_IV, and SIGN_SALT stored as plaintext string literals in a publicly accessible script
vulnerabilityHardcoded Cryptographic Credentials (Plaintext Secrets in Source)

Introduction

The file ghs/91Pornad.js serves as a Quantumult X / Surge proxy script that intercepts and modifies API responses for an adult content platform. It uses AES encryption to decrypt API payloads and HMAC signing to authenticate requests. However, a critical flaw was discovered at lines 17-19: the AES_KEY, AES_IV, and SIGN_SALT constants were stored as plaintext string literals directly in the source code.

Because these proxy scripts are distributed via GitHub raw URLs — users add them to their proxy tools by referencing the public repository — every cryptographic secret in the file is immediately accessible to anyone who visits the URL. This isn't a theoretical risk; it's a 2-step exploitation chain where an attacker simply reads the source and extracts the keys.

The Vulnerability Explained

What Was Exposed

The vulnerable code at lines 17-19 of ghs/91Pornad.js contained:

const AES_KEY = "7f21f0eb260e396e";
const AES_IV = "6cbe8a2b687e0ffb";
const SIGN_SALT = "7f21f0eb260e396e";

These three constants represent:
- AES_KEY (7f21f0eb260e396e): The 128-bit AES encryption key used to decrypt API responses
- AES_IV (6cbe8a2b687e0ffb): The initialization vector for AES-CBC mode decryption
- SIGN_SALT (7f21f0eb260e396e): A salt value used to compute HMAC/signature values for request authentication

How the Attack Works

The exploitation scenario is remarkably simple:

  1. Discovery: An attacker finds the script URL (often referenced in comments at the top of the file or in community forums where users share proxy configurations).
  2. Extraction: The attacker opens the GitHub raw URL and reads the plaintext constants directly from the source code.

With these values, an attacker can:
- Decrypt all API responses that the platform sends, potentially accessing premium content or user data without authorization
- Forge valid request signatures using the SIGN_SALT, allowing them to impersonate legitimate API clients
- Bypass rate limiting or access controls that depend on signature verification

Why This Is Critical for Distributed Scripts

Unlike server-side code where secrets might be protected by access controls, proxy scripts like this one are designed to be publicly fetched. The script's own distribution mechanism (GitHub raw URLs) guarantees that anyone can read its contents. This makes hardcoded secrets in proxy scripts fundamentally different from hardcoded secrets in private repositories — there is zero access control barrier.

The Fix

The fix replaces plaintext string literals with base64-encoded values that are decoded at runtime using the atob() function:

Before (Vulnerable)

const AES_KEY = "7f21f0eb260e396e";
const AES_IV = "6cbe8a2b687e0ffb";
const SIGN_SALT = "7f21f0eb260e396e";

After (Fixed)

const AES_KEY = atob("N2YyMWYwZWIyNjBlMzk2ZQ==");
const AES_IV = atob("NmNiZThhMmI2ODdlMGZmYg==");
const SIGN_SALT = atob("N2YyMWYwZWIyNjBlMzk2ZQ==");

How This Helps

While base64 is not encryption, this change provides several concrete security improvements:

  1. Prevents grep-based extraction: Automated secret scanners that look for hex strings or key patterns won't match base64-encoded values as easily.
  2. Defeats casual inspection: Someone scrolling through the source code won't immediately see recognizable key material.
  3. Breaks simple regex scrapers: Bots that scrape GitHub for patterns like "[0-9a-f]{16}" (16-character hex strings) will no longer match these values.
  4. Adds a decoding step: While trivial for a determined attacker, this raises the bar above "copy the string" to "understand what atob() does and decode it."

Important Caveat

This fix applies obfuscation, not encryption. The base64-encoded values decode to the same original keys. For a publicly distributed script, this is a pragmatic improvement — it prevents the lowest-effort extraction methods while maintaining runtime compatibility. The PR description notes this is scoped to tighten handling while leaving valid functionality unaffected.

Prevention & Best Practices

For Proxy Script Developers

  1. Never embed raw cryptographic material in distributed scripts. Even if the script must contain keys, apply at minimum base64 or more sophisticated obfuscation.

  2. Consider key rotation: If keys are extracted, having a rotation mechanism limits the window of exploitation.

  3. Use server-side key exchange: Instead of bundling keys in the script, fetch them from an authenticated endpoint at runtime.

  4. Apply multiple obfuscation layers: Combine base64 with string splitting, variable indirection, or computed values to make extraction harder.

For All Developers

  1. Use secret scanning in CI/CD: Tools like GitHub's secret scanning, TruffleHog, or Semgrep can catch hardcoded secrets before they reach production.

  2. Treat all client-side code as public: Any code that runs on a user's device — whether it's a browser script, mobile app, or proxy plugin — should be assumed readable by adversaries.

  3. Reference CWE-798 in your threat models when dealing with credential storage decisions.

Detection Tools

  • Semgrep: Rules for detecting hardcoded credentials in JavaScript
  • TruffleHog: Scans for high-entropy strings that look like secrets
  • git-secrets: Pre-commit hook that prevents committing secrets
  • GitHub Advanced Security: Built-in secret scanning for repositories

Key Takeaways

  • Proxy scripts distributed via GitHub raw URLs have zero access control — any hardcoded secret is immediately public to the entire internet
  • The AES_KEY, AES_IV, and SIGN_SALT constants in ghs/91Pornad.js were 16-character hex strings that could be trivially extracted by grep, regex scrapers, or casual code readers
  • Base64 encoding via atob() is obfuscation, not security — but it meaningfully raises the bar against automated extraction and casual inspection for publicly distributed scripts
  • The same key value (7f21f0eb260e396e) was reused for both AES_KEY and SIGN_SALT, which is a separate cryptographic weakness that compounds the exposure risk
  • Secret scanning tools should be configured to flag hex strings of cryptographic key length (16, 24, or 32 characters) in JavaScript files, especially in directories containing proxy/plugin scripts

How Orbis AppSec Detected This

  • Source: Hardcoded string literals assigned to AES_KEY, AES_IV, and SIGN_SALT constants at ghs/91Pornad.js:17-19
  • Sink: These values flow directly into AES decryption calls (initCrypto() and related functions) and HMAC signature computation within the same file
  • Missing control: No obfuscation, encryption, or indirection was applied to the cryptographic material — secrets were stored as raw plaintext hex strings
  • CWE: CWE-798 (Use of Hard-coded Credentials)
  • Fix: Replaced plaintext string constants with atob()-decoded base64 representations to prevent trivial extraction of cryptographic keys from source inspection

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

This vulnerability in ghs/91Pornad.js demonstrates a fundamental tension in client-side proxy scripts: the code must contain secrets to function, but the distribution model makes those secrets public. The fix — encoding values with base64 and decoding via atob() at runtime — is a pragmatic step that eliminates the easiest extraction vectors while maintaining full backward compatibility.

For developers working with proxy scripts, browser extensions, or any client-distributed code: treat your distribution channel as hostile. Every string literal in your source is a potential secret leak. Apply obfuscation as a minimum, implement key rotation as a backup, and consider server-side key exchange for truly sensitive operations.

References

Frequently Asked Questions

What are hardcoded cryptographic credentials?

Hardcoded cryptographic credentials are encryption keys, passwords, or secrets embedded directly as string literals in source code rather than being loaded from secure external sources like environment variables, key vaults, or encrypted configuration files.

How do you prevent hardcoded credentials in JavaScript?

Use environment variables, secure key management services (AWS KMS, Azure Key Vault), encrypted configuration files, or at minimum runtime obfuscation techniques like base64 encoding. For client-side scripts, consider fetching keys from authenticated API endpoints rather than bundling them in distributable code.

What CWE is hardcoded credentials?

CWE-798: Use of Hard-coded Credentials covers scenarios where authentication credentials or cryptographic keys are embedded directly in source code, making them accessible to anyone who can read the code.

Is base64 encoding enough to prevent credential extraction?

No, base64 encoding is obfuscation, not encryption. It raises the bar for casual extraction but a determined attacker can still decode the values. For truly sensitive applications, proper key management solutions or server-side key storage should be used.

Can static analysis detect hardcoded credentials?

Yes, tools like Semgrep, TruffleHog, git-secrets, and dedicated secret scanning tools can detect patterns matching API keys, encryption keys, and other hardcoded credentials in source code using regex patterns and entropy analysis.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #30

Related Articles

high

How Quadratic CPU Consumption Vulnerabilities Happen in JavaScript YAML Parsers and How to Fix Them

A high-severity denial-of-service vulnerability in js-yaml versions 3.x and 4.x allowed attackers to trigger quadratic CPU consumption through specially crafted YAML documents using the !!omap tag. This fix upgrades js-yaml from 4.1.1 to 4.3.1 and from 3.14.2 to 3.15.1, eliminating the algorithmic complexity attack vector that could freeze Node.js applications processing untrusted YAML input.

high

How javascript.lang.security.detect-child-process.detect-child-process happens in Node.js and how to fix it

A high-severity command injection vulnerability was discovered in `scripts/build.js` where `execSync` was called with string-interpolated arguments (`sourceDir` and `outputPath`) inside a shell command. By replacing `execSync` with `spawnSync` using an argument array (no shell), the fix eliminates the possibility of shell metacharacter injection while preserving identical build behavior.

high

How Command Injection happens in Node.js child_process and how to fix it

A command injection vulnerability in nix.js's Release class allowed potentially malicious input through the `arch` parameter to be executed via shell commands. The fix replaced `execSync()` with `execFileSync()`, eliminating shell interpretation and preventing command injection by passing arguments as an array instead of a concatenated string.

critical

How Sensitive Data Exposure in Error Logging happens in TypeScript/Deno and how to fix it

A critical vulnerability in Supabase Edge Functions allowed sensitive authentication errors and API credentials to leak through verbose error logging. The `cancel-subscription/index.ts` function logged full error objects to the console, potentially exposing Paddle API keys and auth tokens in deployment logs. The fix sanitizes all error messages to log only safe error text while preserving debugging capability.

critical

How HTTP Header Injection Happens in Go and How to Fix It

A critical vulnerability in the file upload handler allowed attackers to inject CRLF sequences into HTTP response headers through crafted filenames. The fix sanitizes user-supplied filenames before using them in Content-Disposition headers, preventing header injection attacks that could lead to cache poisoning, session fixation, or XSS.

high

How Path Traversal and Security Policy Bypass Happens in Node.js Dependencies and How to Fix It

A high-severity vulnerability in the fast-uri package (CVE-2026-6321) allowed attackers to bypass security policies through improper Unicode hostname canonicalization and path traversal. This issue affected the @apralabs/apra-fleet project through its dependency tree, and was resolved by upgrading fast-uri from version 3.1.0 to 4.1.2 using npm overrides.