Node.js 在express中提交表单时CSRF令牌不起作用
我正在尝试让表单在我的express应用程序中工作。我有一个中间件函数,它将csrf令牌req.session.\u csrf传递给res.locals.csrf\u令牌,以便视图可以使用它。现在,我试图在视图中使用局部变量,并且从会话中间件中得到一个禁止的错误 这是我的表单代码-我使用把手作为模板引擎:Node.js 在express中提交表单时CSRF令牌不起作用,node.js,express,Node.js,Express,我正在尝试让表单在我的express应用程序中工作。我有一个中间件函数,它将csrf令牌req.session.\u csrf传递给res.locals.csrf\u令牌,以便视图可以使用它。现在,我试图在视图中使用局部变量,并且从会话中间件中得到一个禁止的错误 这是我的表单代码-我使用把手作为模板引擎: <form method='post' action='/api/entries' enctype='multipart/form-data' > <input
<form method='post' action='/api/entries' enctype='multipart/form-data' >
<input type='hidden' name='_csrf' value={{csrf_token}} />
<input class='foo' type='text' />
<input class='bar' type='text' />
<button id='submit' type='submit'> SUBMIT
</form>
在我的应用程序目录中。这是输出。。它看起来不像我在视图之外的任何地方引用它,在视图中,我隐藏的CSRF字段被命名为“_CSRF”
/node\u modules/express/node\u modules/connect/lib/middleware/csrf.js:var secret=req.session.\u csrfSecret;
./node_modules/express/node_modules/connect/lib/middleware/csrf.js:req.session.\u csrfSecret=secret;
./node_modules/express/node_modules/connect/lib/middleware/csrf.js:Object.defineProperty(req.session,“_csrf”{
./node_modules/express/node_modules/connect/lib/middleware/csrf.js:console.warn('req.session.\u csrf已弃用,请改用req.csrfToken());
./node_modules/express/node_modules/connect/lib/middleware/csrf.js:return(req.body&&req.body.\csrf)
./node_modules/express/node_modules/connect/lib/middleware/csrf.js:| | |(req.query和&req.query._csrf)
./v/home.hbs:
./v/show.hbs:
下面是我在尝试发布到/api/entries端点时得到的整个错误堆栈(我之前愚蠢地忽略了提及这一点,但我使用connectredis作为会话中间件):
错误:禁止
在Object.exports.error(appFolder/node_modules/express/node_modules/connect/lib/utils.js:63:13)
在createToken(appFolder/node_modules/express/node_modules/connect/lib/middleware/csrf.js:82:55)
在Object.handle(appFolder/node_modules/express/node_modules/connect/lib/middleware/csrf.js:48:24)
下一步(appFolder/node_modules/express/node_modules/connect/lib/proto.js:193:15)
下一步(appFolder/node_modules/express/node_modules/connect/lib/middleware/session.js:318:9)
在appFolder/node_modules/express/node_modules/connect/lib/middleware/session.js:342:9
在appFolder/node_modules/connect redis/lib/connect redis.js:101:14
在try_回调时(appFolder/node_modules/redis/index.js:580:9)
在RedisClient.return\u reply(appFolder/node\u modules/redis/index.js:670:13)
在ReplyParser(appFolder/node_modules/redis/index.js:312:14)
编辑2:connect-redis.js中的错误是一个函数试图通过会话ID获取当前会话,但失败了。不知道为什么会发生这种情况,我的connect-redis设置看起来是正确的。这让我非常恼火CSRF语法在最新版本的Express/connect中发生了轻微的更改。您现在希望您的中间件像这样:
.use(express.csrf())
.use(function (req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
res.locals.csrftoken = req.csrfToken();
next();
})
为了测试代码,请注意,您首先需要获取表单页面以生成CSRF令牌。只有这样,您的发布才会成功。如果失败,您需要在再次尝试发布之前在浏览器中重新加载页面。编辑:如果不需要上传文件,请不要使用
多部分/表单数据
enctype.Swit切换到默认的enctype将允许express.csrf()
解析\u csrf
令牌
为了使用multipart/form data
enctype解析表单,您需要在应用程序配置中使用多部分解析器,或者自己处理文件上载。建议避免使用附带的express.bodyParser()
并在您希望上传文件的路径上使用类似于busboy
或forgible
的方法,以防止出现错误
如果您这样做,您的\u csrf
字段将不再被express.csrf()
捕获,因为只有在请求通过该中间件之后才会解析表单体。请将表单操作设置为'/api/entries?\u csrf={{csrf\u token}}'
以避免此问题
var fs = require('fs');
var async = require('async');
var express = require('express');
var formidable = require('formidable');
var app = express();
app.use(express.urlencoded())
.use(express.json())
.use(express.cookieParser())
.use(express.session())
.use(express.csrf())
app.get('/upload', function(req, res) {
// File uploads ignored.
res.render('upload', {_csrf:req.csrfToken()});
});
app.post('/upload', function(req, res) {
// Explicitly handle uploads
var form = new formidable.IncomingForm();
form.uploadDir = 'temp';
var count = 0;
var maxAllowed = 10;
form.onPart = function(part) {
if (!part.filename) return form.handlePart(part);
count++;
// Ignore any more files.
if (count > maxAllowed) return part.resume();
form.handlePart(part);
};
form.parse(req, function(err, fields, files) {
// Process the files. If you don't need them, delete them.
// Note that you should still reap your temp directory on occasion.
async.map(Object.keys(files), function(key, cb) {
fs.unlink(files[key].path, cb);
}, function(err) {
res.end();
});
});
});
我今天也遇到了这个问题,花了几个小时才找到解决方案。希望这个答案能帮助别人解决我的问题。作为@amagumori,我正在使用redis进行会话处理,express 3.4.8,连接redis 1.4.7 基本上,我能够确定express配置的顺序会影响新令牌的发行次数。似乎所有公共服务都在创建新令牌 特别是在我的情况下,我必须移动电话
app.use(express.methodOverride());
app.use(express.bodyParser());
app.use(express.static(__dirname + '/public'));
在上面
app.use(express.csrf());
app.use(function(req, res, next){
res.locals.token = req.csrfToken();
next();
});
并且会按预期为会话发出令牌。您如何呈现模板?您正在传递值
csrf_token
?我的express配置中有一个函数,看起来像:app.use(函数(req,res,next){res.locals.csrf_token=req.session.\u csrf;next();})我尝试显式地将其添加到res.render()中包含的局部对象,但没有成功。render
参数是什么?{csrf\u token:xyz}
?通过这种方式实现,现在我遇到了一个类型错误:当我尝试使用表单获取页面时,Object没有方法“csrfToken”,因此我使用的是express 3.3.4,请升级到3.4.6,然后再试一次。我遇到了同样的问题,正在尽我所能解决。你解决了吗?嘿,使用这种策略,这不是res.locals.csrfToken=req、 csrfToken()
每次为每个请求生成一个新的令牌?如果我们只在会话期间生成一个令牌可以吗?这样做的最佳方式是什么?谢谢!我在使用repo hachathon-starter时遇到了相同的问题。将表单操作设置为书面形式对我来说是解决问题的方法。但是,在很少的情况下,我注意到我仍然是g设置禁止的错误。经过一些调查,我意识到我必须在表单操作中编码csrf值。
var fs = require('fs');
var async = require('async');
var express = require('express');
var formidable = require('formidable');
var app = express();
app.use(express.urlencoded())
.use(express.json())
.use(express.cookieParser())
.use(express.session())
.use(express.csrf())
app.get('/upload', function(req, res) {
// File uploads ignored.
res.render('upload', {_csrf:req.csrfToken()});
});
app.post('/upload', function(req, res) {
// Explicitly handle uploads
var form = new formidable.IncomingForm();
form.uploadDir = 'temp';
var count = 0;
var maxAllowed = 10;
form.onPart = function(part) {
if (!part.filename) return form.handlePart(part);
count++;
// Ignore any more files.
if (count > maxAllowed) return part.resume();
form.handlePart(part);
};
form.parse(req, function(err, fields, files) {
// Process the files. If you don't need them, delete them.
// Note that you should still reap your temp directory on occasion.
async.map(Object.keys(files), function(key, cb) {
fs.unlink(files[key].path, cb);
}, function(err) {
res.end();
});
});
});
app.use(express.methodOverride());
app.use(express.bodyParser());
app.use(express.static(__dirname + '/public'));
app.use(express.csrf());
app.use(function(req, res, next){
res.locals.token = req.csrfToken();
next();
});