How Hardcoded Secrets Compromise Authentication in JavaScript and How to Fix It
Introduction
In the Tool/QuantumultX/Rewrite/RRSP.js file, a critical vulnerability exposed authentication credentials directly in source code. The USER_CONFIG object on lines 27-28 contained a hardcoded TOKEN (rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993) and UMID device identifier (C6CFE97D-A15C-4FE0-8666-FB0036C5E32A) used to authenticate with the RRTV API service. This vulnerability created a 2-step attack chain: an attacker could extract these credentials from the public repository and immediately use them to make authenticated API requests to api.rrmj.plus, gaining complete access to the user's account.
For developers working with API integrations and configuration management, this vulnerability illustrates a fundamental security principle: credentials must never live in code. Even in "private" scripts or seemingly internal tools, hardcoded secrets are a liability the moment code is shared, archived, or exposed.
The Vulnerability Explained
What Made This Code Vulnerable?
The vulnerable code in RRSP.js looked like this:
// 配置
let USER_CONFIG = {
TOKEN: "rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993",
UMID: "C6CFE97D-A15C-4FE0-8666-FB0036C5E32A",
PLAYER_Code: "SenPlayer",
CustomScheme: ""
};
The specific problem:
- Lines 27-28 contain literal credential strings embedded in configuration that will be read into memory every time the script executes
- The TOKEN follows a predictable pattern (
rrtv-prefix), making it easily identifiable as an authentication secret - The UMID is a UUID format, another recognizable pattern that credential scanners detect
- These values are committed to the repository, making them accessible to anyone with clone/fork permissions—forever in Git history unless actively purged
- No environment variable or external configuration mechanism exists to override these defaults, forcing users to either modify source code or accept the exposed credentials
How This Could Be Exploited
An attacker following this 2-step chain:
- Discovery: Clone or fork the public repository containing
RRSP.js, scan the file, and extract the TOKEN and UMID strings from lines 27-28 - Exploitation: Use these credentials in HTTP requests to
api.rrmj.plus:
javascript fetch('https://api.rrmj.plus/endpoint', { headers: { 'Authorization': `Bearer ${TOKEN}`, 'X-UMID': UMID } })
The API would recognize these credentials as valid and authenticate the request as the legitimate user, granting full access to account data, content library, viewing history, and any other RRTV API functionality
Real-World Impact for RRSP.js
- Account Takeover: The attacker gains full control over the RRTV account associated with these credentials
- Data Breach: Access to all content watched, bookmarks, preferences, and personal viewing history
- Service Abuse: Using the account to stream premium content without authorization, potentially triggering false fraud alerts or account suspension for the legitimate user
- Persistence: Without rotating credentials, the compromise is persistent until the legitimate user manually changes their token
- Supply Chain Risk: If this script is shared across teams or embedded in other tools, the compromise spreads to all downstream users
The Fix
What Changed?
The pull request replaced the hardcoded credentials with empty string defaults and added inline comments instructing users to manually fill in values:
// BEFORE (vulnerable)
let USER_CONFIG = {
TOKEN: "rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993",
UMID: "C6CFE97D-A15C-4FE0-8666-FB0036C5E32A",
PLAYER_Code: "SenPlayer",
CustomScheme: ""
};
// AFTER (fixed)
let USER_CONFIG = {
TOKEN: "", //手动填写频道更新的token
UMID: "", //手动填写..
PLAYER_Code: "SenPlayer",
CustomScheme: ""
};
Why this specific change works:
- Empty defaults prevent automatic credential exposure: No credentials live in the repository or Git history
- Credentials remain outside version control: Users manually configure their own TOKEN and UMID at runtime or through external configuration
- Shifts responsibility to users: The script won't function without explicit user configuration, preventing accidental use of leaked credentials
- No hardcoded patterns to detect: Secret scanning tools won't flag empty strings, reducing false positives
- Eliminates 2-step attack chain: Even if an attacker accesses the repository, no valid credentials exist to extract
How the Fix Solves This Specific Problem
Before the fix:
- Credentials: Always present, always exploitable, always in Git history
- Exploit complexity: Trivial (copy-paste from source)
- Blast radius: Everyone who cloned the repo
After the fix:
- Credentials: Never stored in code or Git
- Exploit complexity: Impossible from repository alone; attacker must compromise the user's local configuration
- Blast radius: Limited to individual user's local machine
The fix follows the principle of least privilege in configuration: the script ships with no secrets, and users must consciously provide them during setup.
Prevention & Best Practices
To prevent this vulnerability in similar code:
1. Use Environment Variables
let USER_CONFIG = {
TOKEN: process.env.RRTV_TOKEN || "",
UMID: process.env.RRTV_UMID || "",
PLAYER_Code: "SenPlayer",
CustomScheme: ""
};
Users set export RRTV_TOKEN=<token> before running the script.
2. Use External Configuration Files (Outside .gitignore)
const fs = require('fs');
const path = require('path');
// Load from ~/.config/rrsp/config.json
const configPath = path.join(process.env.HOME, '.config/rrsp/config.json');
const userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
Add ~/.config/rrsp/config.json to .gitignore.
3. Implement Secret Scanning Pre-Commit Hooks
Use tools like TruffleHog or detect-secrets to block commits containing credential patterns:
npm install --save-dev @trufflesecurity/trufflehog
# Or
npm install --save-dev detect-secrets
4. Use Semgrep Rules to Catch Hardcoded Secrets
Deploy Semgrep rule javascript.lang.security.hardcoded-secrets to catch patterns like:
TOKEN: "[a-f0-9]{40,}" # 40+ hex chars (common token format)
5. Follow OWASP Guidelines
Reference OWASP's Secrets Management Cheat Sheet for best practices on credential storage and rotation.
6. Rotate Exposed Credentials Immediately
If hardcoded credentials are ever committed, they must be assumed compromised. Rotate the TOKEN and UMID in the RRTV API settings immediately.
7. Audit Git History
Use git log -S "rrtv-" to find all commits containing the leaked token pattern and ensure they're removed from all branches.
Key Takeaways
-
Hardcoded tokens in JavaScript configuration objects are trivial to extract and exploit: The
USER_CONFIGobject in RRSP.js lines 27-28 exposed credentials directly to anyone with repository access—no decryption or reverse engineering needed. -
Credential patterns are predictable and easily scannable: The
rrtv-prefix and UUID format in the UMID made this vulnerability detectable by automated secret scanning tools, showing why secrets should be externalized before code review. -
Empty string defaults with documentation shift security responsibility: Replacing hardcoded values with
""and comments forces users to consciously configure credentials, preventing accidental deployment of exposed secrets. -
2-step account takeover chains are high-risk but preventable: This vulnerability required only repository access + credential usage—a trivial 2-step exploit that environment variables or secret managers completely eliminate.
-
Version control history is permanent: Deleting hardcoded secrets from the current branch isn't enough; they persist in Git history unless actively purged with tools like
git-filter-repo, so prevention at commit time is essential.
How Orbis AppSec Detected This
Orbis AppSec identified this vulnerability through automated secret pattern analysis:
- Source: Configuration object
USER_CONFIGon lines 27-28 ofTool/QuantumultX/Rewrite/RRSP.jscontaining hardcoded credential literals - Sink: Direct assignment of sensitive strings to
TOKENandUMIDproperties without external sourcing or sanitization - Missing control: No environment variable lookup, no external config file loading, no secret manager integration—credentials embedded directly in code
- CWE: CWE-798: Use of Hard-Coded Credentials — sensitive authentication information stored in source code accessible to attackers with code repository access
- Fix: Replaced hardcoded token string
"rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993"and UMID"C6CFE97D-A15C-4FE0-8666-FB0036C5E32A"with empty defaults (""), eliminating credentials from version control and requiring manual user configuration
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
Hardcoded secrets in JavaScript configuration files represent one of the easiest vulnerabilities to exploit and one of the most damaging once compromised. The RRSP.js vulnerability demonstrates that even in specialized or internal tools, credentials embedded in source code create an immediate account takeover risk.
The fix—replacing hardcoded values with empty defaults—is simple but requires discipline in your development workflow. Combined with environment variables, external configuration management, and automated secret scanning, this approach eliminates the vulnerability class entirely.
For your own projects:
1. Audit your codebase for hardcoded secrets using git log -S and Semgrep
2. Implement .gitignore rules and pre-commit hooks to prevent future commits
3. Use environment variables or secret managers for all credentials
4. Rotate any exposed credentials immediately
5. Remove secrets from Git history using git-filter-repo
Secure authentication starts with keeping credentials out of code. Make that your first principle, and you'll prevent vulnerabilities before they become incidents.