Back to Blog
critical SEVERITY5 min read

How hardcoded API key exposure happens in JavaScript and how to fix it

A critical security vulnerability was discovered in `js/douban.js` where the Douban API key (`0ac44ae016490db2204ce0a042db2916`) was hardcoded directly in client-side JavaScript. This exposed the API credential to any user who could view the page source or use browser developer tools. The fix removed the hardcoded key, preventing unauthorized API access and potential abuse.

O
By Orbis AppSec
Published July 28, 2026Reviewed July 28, 2026

Answer Summary

This vulnerability is a hardcoded API key exposure (CWE-798) in JavaScript where the Douban miniapp API key was embedded directly in client-side code at `js/douban.js:697`. Any user could extract this key using browser developer tools. The fix removes the hardcoded value by setting `miniapp_apikey=""`, requiring the key to be provided through a secure server-side mechanism instead.

Vulnerability at a Glance

cweCWE-798
fixRemove hardcoded key and implement server-side credential management
riskUnauthorized API access, quota abuse, service impersonation
languageJavaScript
root causeAPI key embedded directly in client-side JavaScript code
vulnerabilityHardcoded API Key Exposure

Introduction

In the js/douban.js file, we discovered a critical hardcoded API key exposure at line 697. The Douban miniapp API key (miniapp_apikey="0ac44ae016490db2204ce0a042db2916") was embedded directly in a JavaScript string that gets delivered to every user's browser. This file handles integration with the Douban API service for fetching movie and TV show data, but the credential management approach created a severe security risk.

The problematic code was part of a minified JavaScript block within the 推荐 property of a rule object:

let miniapp_apikey="0ac44ae016490db2204ce0a042db2916";

This matters because any developer working with third-party API integrations needs to understand: client-side code is never a safe place for credentials.

The Vulnerability Explained

When JavaScript runs in a browser, the source code is completely accessible to users. Anyone can:

  1. Right-click and "View Page Source"
  2. Open browser Developer Tools (F12) and inspect the Sources tab
  3. Use the Network tab to see API requests with the key attached

In this specific case, the miniapp_request function in douban.js used the hardcoded key for every API call:

function miniapp_request(path, query) {
    try {
        let url = douban_api_host + path;
        query.apikey = miniapp_apikey;  // Hardcoded key attached to every request
        fetch_params.headers = oheaders;
        url = buildUrl(url, query);
        let html = fetch(url, fetch_params);
        return JSON.parse(html);
    } catch(e) {
        print("发生了错误:" + e.message);
        return {};
    }
}

Attack Scenario

An attacker targeting this application could:

  1. Extract the key: Open js/douban.js in browser DevTools, search for "apikey", and copy 0ac44ae016490db2204ce0a042db2916
  2. Abuse the API quota: Make unlimited requests to Douban's API using the stolen key, potentially exhausting rate limits for legitimate users
  3. Impersonate the application: Use the key to access Douban API endpoints, potentially accessing data or functionality meant only for this application
  4. Cause financial damage: If the API key is tied to a paid tier, the attacker could rack up charges against the application owner

Real-World Impact

For this Douban integration specifically:
- The subject_real_time_hotest function fetches trending movies and TV shows
- An attacker could scrape Douban's entire catalog using the exposed key
- The legitimate application could be rate-limited or banned due to abuse
- If Douban tracks API usage, the application owner could face account suspension

The Fix

The fix was straightforward but essential—remove the hardcoded API key entirely:

Before (Vulnerable)

let miniapp_apikey="0ac44ae016490db2204ce0a042db2916";

After (Fixed)

let miniapp_apikey="";

This change in js/douban.js at line 697 ensures that:

  1. No credential is exposed in client-side code
  2. The API integration breaks intentionally until a proper server-side solution is implemented
  3. The attack surface is eliminated immediately

The fix deliberately leaves the variable empty rather than removing the code entirely. This preserves the application structure while forcing developers to implement proper credential management through a secure backend proxy or environment variable injection at build time.

Why This Approach Works

By setting miniapp_apikey="", the code still runs without syntax errors, but API calls will fail authentication. This is the correct behavior because:

  • It prevents the immediate security risk
  • It makes the broken functionality obvious during testing
  • It forces the implementation of a proper secure solution

Prevention & Best Practices

1. Never Embed Secrets in Client-Side Code

This is the golden rule. Anything in JavaScript, HTML, or CSS that reaches the browser is public.

2. Use a Backend Proxy

Instead of calling external APIs directly from the browser:

// ❌ Bad: Direct API call with exposed key
fetch('https://api.douban.com/v2/movie?apikey=SECRET_KEY');

// ✅ Good: Call your own backend, which holds the key securely
fetch('/api/proxy/douban/movie');

Your backend then makes the actual API call with credentials stored in environment variables.

3. Environment Variables at Build Time

For static sites, inject API keys during the build process, but only for keys that are safe to expose (like public analytics IDs):

// Only for truly public keys (e.g., Google Maps public API key)
const publicKey = process.env.REACT_APP_PUBLIC_KEY;

4. Use Secret Scanning Tools

Implement pre-commit hooks and CI/CD checks that scan for credential patterns:

  • git-secrets: Prevents committing secrets
  • truffleHog: Scans git history for high-entropy strings
  • Semgrep: Static analysis with secret detection rules

5. Rotate Compromised Keys Immediately

If a key has been exposed (like this Douban key), assume it's compromised:

  1. Generate a new API key in the service's dashboard
  2. Update your secure backend configuration
  3. Revoke the old key
  4. Monitor for unauthorized usage

Key Takeaways

  • The miniapp_apikey in douban.js was visible to every user who loaded the page, making extraction trivial
  • Client-side JavaScript is never secure for credentials—treat all browser code as public
  • The miniapp_request function attached the exposed key to every Douban API call, amplifying the risk
  • Removing the hardcoded value forces proper architecture—a backend proxy should handle API authentication
  • This exact pattern (apikey="..." in JS) is easily detectable with static analysis, making prevention straightforward

How Orbis AppSec Detected This

  • Source: The hardcoded string "0ac44ae016490db2204ce0a042db2916" assigned to miniapp_apikey in js/douban.js:697
  • Sink: The query.apikey = miniapp_apikey assignment in the miniapp_request function, which attached the credential to outgoing API requests
  • Missing control: No server-side proxy or secure credential injection mechanism; the API key was embedded directly in deliverable client-side code
  • CWE: CWE-798 (Use of Hard-coded Credentials)
  • Fix: Removed the hardcoded API key value, setting miniapp_apikey="" to eliminate client-side credential exposure

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 API keys in client-side JavaScript represent one of the most common and easily exploitable security vulnerabilities. The Douban API key exposure in js/douban.js demonstrates how a single line of code—miniapp_apikey="0ac44ae016490db2204ce0a042db2916"—can compromise an entire API integration.

The fix is simple: never put secrets in client-side code. Use backend proxies, environment variables on the server, and automated scanning tools to catch these issues before they reach production. Remember, if code runs in a browser, users can read it.

References

Frequently Asked Questions

What is hardcoded API key exposure?

Hardcoded API key exposure occurs when authentication credentials are embedded directly in source code, making them accessible to anyone who can view the code, especially in client-side applications.

How do you prevent hardcoded API key exposure in JavaScript?

Never embed API keys in client-side code. Use environment variables, server-side proxies, or secure backend services to handle API authentication without exposing credentials to browsers.

What CWE is hardcoded API key exposure?

CWE-798: Use of Hard-coded Credentials covers this vulnerability, which describes the risk of embedding authentication data directly in source code.

Is obfuscating JavaScript enough to prevent API key exposure?

No, obfuscation only makes extraction harder, not impossible. Determined attackers can still reverse-engineer obfuscated code or intercept network requests to obtain credentials.

Can static analysis detect hardcoded API keys?

Yes, static analysis tools can detect patterns like hardcoded strings assigned to variables named "apikey", "secret", or "token", making this vulnerability highly detectable during code review.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1

Related Articles

medium

How Insufficient Password Hashing Cost Factor Happens in Node.js and How to Fix It

A bcrypt password hashing implementation in the User.js model was using a cost factor of 10, which falls below OWASP's 2024 recommendation of 12 for applications handling sensitive data. This fix upgrades the salt rounds from 10 to 12, increasing the computational work required to crack passwords by approximately 4x, significantly improving protection against brute-force attacks on this e-commerce platform.

high

How Arbitrary Code Execution via Template Imports Happens in JavaScript (lodash) and How to Fix It

A high-severity arbitrary code execution vulnerability (CVE-2026-4800) was discovered in lodash's template function, specifically in how it handles the `imports` option with untrusted input. The fix upgrades lodash from version 4.17.21 to 4.18.0 in the project's `package.json` and `yarn.lock`, eliminating the attack surface where crafted template imports could execute arbitrary code on the server.

critical

How Server-Side Request Forgery (SSRF) happens in Node.js IP address parsing and how to fix it

A critical SSRF vulnerability (CVE-2026-69192) was discovered in the ip-address npm package version 10.2.0, which could allow attackers to bypass IP address validation and access internal services. The fix upgrades the dependency to version 10.3.1, which properly handles edge cases in IP address parsing that previously allowed trust-boundary bypasses.

critical

How Sensitive Data Exposure happens in Python web applications and how to fix it

A critical sensitive data exposure vulnerability was discovered in `nodes/google_gemini.py` where the Google Gemini API key was returned in plaintext through a web endpoint. The fix masks the token in API responses, preventing credential theft from any client that queries the token endpoint. This protects downstream users of this Node.js library from unauthorized access to their Google Gemini services.

high

How Authentication Bypass happens in Next.js App Router with Turbopack and how to fix it

A critical authentication bypass vulnerability (CVE-2026-64642) was discovered in Next.js versions prior to 16.2.11, specifically affecting App Router applications using Turbopack with a single locale configuration. This vulnerability allowed attackers to bypass middleware and proxy protections, potentially gaining unauthorized access to protected routes and resources that should have been secured by authentication checks.

critical

How SQL Injection Happens in CSV-to-SQL Converters and How to Fix It

A critical SQL injection vulnerability was discovered in the `csv2sql()` function in `src/data/converter/csv.js`, where CSV data and table names were directly interpolated into SQL INSERT statements without sanitization. The fix implements input validation through identifier sanitization and proper value escaping, eliminating the attack surface while preserving legitimate functionality.