You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现JavaScript代码安全:API Key不公开且可正常调用?

前端订阅功能的API密钥泄露问题与修复方案

问题背景

下面这段JavaScript代码原本用来实现Mailchimp邮件订阅功能,但存在严重的安全漏洞:

const form = document.querySelector('.subsform');
const submitButton = form.querySelector('input[type="button"]');
const messageDiv = document.querySelector('#newsletter-message');

submitButton.addEventListener('click', (event) => {
  event.preventDefault();
  
  const firstName = form.querySelector('input[placeholder="First Name"]').value;
  const lastName = form.querySelector('input[placeholder="Last Name"]').value;
  const email = form.querySelector('input[type="email"]').value;
  const apiKey = form.querySelector('input[name="mailchimpAPIkey"]').getAttribute('data-api');
  const listId = form.querySelector('input[name="mailChimpListId"]').getAttribute('data-listId');
  
  console.log({ firstName, lastName, email, apiKey, listId });
  
  const url = `https://us20.api.mailchimp.com/3.0/lists/${listId}/members/`;
  const data = {
    email_address: email,
    status: 'subscribed',
    merge_fields: {
      FNAME: firstName,
      LNAME: lastName
    }
  };
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${btoa(`apikey:${apiKey}`)}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  };
  
  fetch(url, options)
    .then(response => response.json())
    .then(data => {
      messageDiv.innerHTML = `Thank you for subscribing, ${firstName}!`;
      console.log(data);
    })
    .catch(error => {
      messageDiv.innerHTML = 'There was an error subscribing. Please try again later.';
      console.error(error);
    });
});

只要懂点基础JS的用户,就能通过控制台执行以下代码,轻松获取页面里的API密钥和列表ID等敏感信息:

// 选中页面所有隐藏input
const hiddenInputs = document.querySelectorAll('input[type="hidden"]');

// 遍历并打印到控制台
hiddenInputs.forEach(input => {
  console.log(input);
});

漏洞根源

前端页面的任何数据(包括隐藏元素的属性值)都是完全公开的,用户可以通过开发者工具直接查看。把API密钥这类敏感信息放在前端,等于把你的Mailchimp账户权限拱手让人,别人拿到密钥后可以随意操作你的邮件列表,甚至造成不必要的损失。

修复方案:后端中转

核心思路是绝对不让前端接触敏感凭证,所有需要调用第三方API的操作,都通过自己的后端接口中转完成。

1. 改造前端代码

移除所有和敏感信息相关的逻辑,前端只负责收集用户的姓名、邮箱,然后把数据发给自己的后端接口:

const form = document.querySelector('.subsform');
const submitButton = form.querySelector('input[type="button"]');
const messageDiv = document.querySelector('#newsletter-message');

submitButton.addEventListener('click', (event) => {
  event.preventDefault();
  
  const firstName = form.querySelector('input[placeholder="First Name"]').value;
  const lastName = form.querySelector('input[placeholder="Last Name"]').value;
  const email = form.querySelector('input[type="email"]').value;

  // 先做基础输入验证(可选但推荐)
  if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
    messageDiv.textContent = '请输入有效的邮箱地址';
    return;
  }
  
  // 调用自己的后端订阅接口
  fetch('/api/subscribe', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ firstName, lastName, email })
  })
  .then(response => response.json())
  .then(result => {
    if (result.success) {
      messageDiv.textContent = `Thank you for subscribing, ${firstName}!`;
      form.reset(); // 订阅成功后重置表单
    } else {
      messageDiv.textContent = result.message || '订阅失败,请稍后重试';
    }
  })
  .catch(error => {
    messageDiv.textContent = '网络错误,请稍后重试';
    console.error(error);
  });
});

2. 实现后端中转接口

这里以Node.js + Express为例,其他后端语言(PHP、Python、Java等)思路完全一致:

首先安装依赖:

npm install express axios dotenv

然后编写后端代码:

const express = require('express');
const axios = require('axios');
require('dotenv').config(); // 加载环境变量

const app = express();
app.use(express.json()); // 解析JSON请求体

// 从环境变量读取敏感信息(绝对不要硬写在代码里!)
const MAILCHIMP_API_KEY = process.env.MAILCHIMP_API_KEY;
const MAILCHIMP_LIST_ID = process.env.MAILCHIMP_LIST_ID;
const MAILCHIMP_SERVER = process.env.MAILCHIMP_SERVER; // 比如"us20",对应API密钥末尾的后缀

// 订阅接口
app.post('/api/subscribe', async (req, res) => {
  try {
    const { firstName, lastName, email } = req.body;

    // 调用Mailchimp官方API
    const mcResponse = await axios.post(
      `https://${MAILCHIMP_SERVER}.api.mailchimp.com/3.0/lists/${MAILCHIMP_LIST_ID}/members/`,
      {
        email_address: email,
        status: 'subscribed',
        merge_fields: {
          FNAME: firstName,
          LNAME: lastName
        }
      },
      {
        auth: {
          username: 'apikey', // Mailchimp要求用户名固定为apikey
          password: MAILCHIMP_API_KEY
        }
      }
    );

    res.json({ success: true, data: mcResponse.data });
  } catch (error) {
    console.error('Mailchimp请求失败:', error.response?.data || error.message);
    // 把友好的错误信息返回给前端
    const errorMsg = error.response?.data?.title || '订阅失败,请稍后重试';
    res.status(500).json({ success: false, message: errorMsg });
  }
});

// 启动服务
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务运行在端口 ${PORT}`);
});

3. 配置环境变量

创建一个.env文件,用来存储敏感信息:

MAILCHIMP_API_KEY=你的MailchimpAPI密钥
MAILCHIMP_LIST_ID=你的邮件列表ID
MAILCHIMP_SERVER=us20 # 替换成你密钥对应的服务器前缀

记得把.env加入到.gitignore里,绝对不要提交到代码仓库!

额外注意事项

  • 后端要做输入校验和限流,防止恶意批量提交垃圾邮箱。
  • 处理Mailchimp的各种错误场景,比如用户已订阅、邮箱格式无效等,给用户更精准的提示。
  • 如果是静态网站,可以用Serverless函数(比如Vercel Functions、Netlify Functions)来实现后端逻辑,不用单独搭建服务器。

内容的提问来源于stack exchange,提问作者WordCent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:42:57