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

如何实现仅允许自身网站代码向Node.js/Express.js后端发起POST API请求

嘿,这个问题确实是前后端通信里的经典痛点——毕竟前端代码全在客户端,想藏密钥根本是白费功夫。结合你用Express的场景,我给你几个经过实战验证的可行方案,从基础防御到敏感操作加固都有:

这是应对这类需求的标准方案,核心思路是验证请求确实来自你的网站页面,而非第三方脚本或工具。

实现步骤:

  1. 后端生成随机CSRF令牌,存入HttpOnly+SameSite类型的Cookie(HttpOnly防止前端JS读取,SameSite限制跨站请求携带Cookie)
  2. 页面加载时,后端把令牌嵌入到页面DOM中(比如meta标签),让前端能获取到
  3. 前端发POST请求时,将令牌放到自定义请求头里(比如X-CSRF-Token)
  4. 后端中间件验证请求头的令牌与Cookie中的令牌是否一致

Express代码示例:

const express = require('express');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
const app = express();

app.use(cookieParser());
app.use(express.json());

// 页面路由:生成CSRF令牌并传给前端
app.get('/', (req, res) => {
  const csrfToken = crypto.randomBytes(32).toString('hex');
  // 设置Cookie:生产环境记得开启secure(HTTPS下生效)
  res.cookie('csrfToken', csrfToken, {
    httpOnly: true,
    sameSite: 'strict',
    secure: process.env.NODE_ENV === 'production',
    maxAge: 3600000 // 1小时有效期
  });
  // 把令牌嵌入页面meta标签
  res.send(`
    <html>
      <body>
        <meta name="csrf-token" content="${csrfToken}">
        <button onclick="submitData()">提交数据</button>
        <script>
          function submitData() {
            const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
            fetch('/api/submit', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
                'X-CSRF-Token': csrfToken
              },
              body: JSON.stringify({ username: 'test' })
            });
          }
        </script>
      </body>
    </html>
  `);
});

// CSRF验证中间件
const validateCsrf = (req, res, next) => {
  const cookieToken = req.cookies.csrfToken;
  const headerToken = req.headers['x-csrf-token'];
  if (!cookieToken || !headerToken || cookieToken !== headerToken) {
    return res.status(403).json({ error: 'Invalid CSRF token' });
  }
  next();
};

// 受保护的POST接口
app.post('/api/submit', validateCsrf, (req, res) => {
  res.json({ success: true, receivedData: req.body });
});

app.listen(3000);

优势:

第三方无法跨域读取你的页面内容(同源策略限制),也就拿不到令牌;同时SameSite Cookie会阻止跨站请求携带Cookie,双重保障确保只有你的网站能合法提交请求。

方案2:验证请求来源(辅助防御)

通过检查请求的Origin或Referer头,判断请求是否来自你的域名。虽然这两个头可能被伪造,但浏览器在正常跨站请求时会限制Origin的传递,所以可以作为第一层防御。

Express代码示例:

const validateOrigin = (req, res, next) => {
  // 配置你的合法域名列表
  const allowedOrigins = ['https://your-production-domain.com', 'http://localhost:3000'];
  const origin = req.headers.origin;
  const referer = req.headers.referer;

  // 优先验证Origin,再验证Referer
  if (origin && allowedOrigins.includes(origin)) {
    return next();
  }
  if (referer && allowedOrigins.some(allowed => referer.startsWith(allowed))) {
    return next();
  }
  return res.status(403).json({ error: 'Invalid request origin' });
};

// 给POST接口加上来源验证
app.post('/api/submit', validateOrigin, (req, res) => {
  res.json({ success: true });
});

注意:

生产环境记得移除开发环境的localhost域名,且不要单独依赖这个方案,要和CSRF令牌配合使用。

方案3:短时效一次性令牌(敏感操作专用)

如果是支付、修改密码这类高敏感操作,可以用一次性、短时效的令牌:用户触发操作前先请求后端获取临时令牌,提交时携带该令牌,后端验证后立即失效。

Express代码示例:

const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());

// 用Set存储临时令牌(生产环境建议用Redis)
const tempTokens = new Set();

// 获取临时令牌接口
app.get('/api/get-temp-token', (req, res) => {
  const tempToken = crypto.randomBytes(16).toString('hex');
  tempTokens.add(tempToken);
  // 5分钟后自动过期
  setTimeout(() => tempTokens.delete(tempToken), 300000);
  res.json({ tempToken });
});

// 敏感操作接口
app.post('/api/submit-sensitive', (req, res) => {
  const tempToken = req.headers['x-temp-token'];
  if (!tempToken || !tempTokens.has(tempToken)) {
    return res.status(403).json({ error: 'Invalid temporary token' });
  }
  tempTokens.delete(tempToken); // 令牌只能用一次
  res.json({ success: true, message: '敏感操作已执行' });
});

前端代码示例:

async function submitSensitiveData() {
  // 先获取临时令牌
  const tokenRes = await fetch('/api/get-temp-token');
  const { tempToken } = await tokenRes.json();
  // 携带令牌提交请求
  await fetch('/api/submit-sensitive', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Temp-Token': tempToken
    },
    body: JSON.stringify({ password: 'new-password' })
  });
}

优势:

即使令牌被窃取,也会很快过期且无法重复使用,极大降低风险。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:32:46