如何让Node.js+Express应用的API仅接受自身页面的AJAX请求?
实现Express API仅接受自站页面AJAX请求的最佳方案
要实现API仅允许自站页面发起的AJAX请求调用,核心是利用浏览器的同源策略结合令牌验证,单纯检查Origin头不可靠(易伪造),以下是可行的方案:
一、CSRF令牌验证(最佳方案)
这是目前最可靠的方式,通过绑定会话与请求令牌,确保请求来自自站渲染的页面:
实现步骤:
- 配置Session存储用户会话信息,用于保存CSRF令牌
- 渲染页面时生成CSRF令牌,注入到页面的JS中
- AJAX请求时携带该令牌,API接口验证令牌与Session中存储的是否匹配
代码示例(使用csurf中间件):
首先安装依赖:
npm install csurf express-session
Express配置代码:
const express = require('express'); const session = require('express-session'); const csrf = require('csurf'); const app = express(); // 配置Session,生产环境建议替换为Redis等持久化存储 app.use(session({ secret: 'your-strong-secret-key', resave: false, saveUninitialized: true, cookie: { secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS时启用 httpOnly: true } })); // 初始化CSRF中间件,令牌存储在Session中 const csrfProtection = csrf({ cookie: false }); // 渲染页面时注入CSRF令牌 app.get('/homepage', csrfProtection, (req, res) => { res.send(` <html> <body> <input type="text" id="userInput"> <button onclick="submitRequest()">提交</button> <script> async function submitRequest() { const inputVal = document.getElementById('userInput').value; const response = await fetch('/api/getinfo', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': '${req.csrfToken()}' }, body: JSON.stringify({ input: inputVal }) }); const data = await response.json(); console.log(data); } </script> </body> </html> `); }); // API接口验证CSRF令牌 app.post('/api/getinfo', csrfProtection, express.json(), (req, res) => { res.json({ status: 'success', message: '请求来自自站页面', input: req.body.input }); }); app.listen(3000, () => console.log('服务运行在3000端口'));
原理:
- 浏览器访问
/homepage时,服务器生成唯一CSRF令牌并绑定到当前Session - 页面中的AJAX请求会自动携带同域Session Cookie,同时在请求头中带上令牌
- API接口验证请求头的令牌与Session中的令牌是否一致,不一致则拒绝请求
- 直接通过Postman等工具调用API时,无法获取有效Session和匹配的令牌,无法通过验证
二、辅助验证:检查Referer头
虽然Referer头也可被伪造,但对于普通场景能增加一层防护,建议与CSRF令牌结合使用:
// 给API路由添加Referer检查中间件 app.use('/api', (req, res, next) => { const referer = req.get('Referer'); const allowedHost = `${req.protocol}://${req.get('host')}`; // 验证Referer是否来自自站 if (referer?.startsWith(allowedHost)) { next(); } else { res.status(403).json({ error: '禁止直接访问API' }); } });
注意:部分浏览器在HTTPS转HTTP、隐私模式下可能不发送Referer头,因此不能单独依赖此方法。
为什么Origin头不可靠?
Origin头可通过工具(如Postman)手动设置为目标域名,完全无法阻止伪造请求,因此单独使用Origin检查没有实际防护作用。
内容的提问来源于stack exchange,提问作者kgaspard
相关产品推荐
相关产品推荐

