在快递中提交表单时,CSRF令牌不起作用

我试图让我的快速应用程序中的表单工作。 我有一个中间件函数,将csrf标记req.session._csrf传递给res.locals.csrf_token,以便视图可以使用它。 现在我试图在我的视图中使用本地variables,我从我的会话中间件得到一个禁止的错误。

这里是我的表单代码 – 我使用把手作为我的模板引擎:

<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_tokenvariables有和没有双花括号,都不起作用。 任何想法,我做错了什么? 错误:禁止发生之前我的路由functionPOST /到/ api /条目甚至被称为。 所以我很确定问题是,我做错了引用csrf标记的东西..

* 编辑: *关于“req.session._csrf被弃用,使用req.csrfToken(),而不是”login到控制台,我做了:

 grep -r '_csrf' . 

在我的应用程序目录。 这里是输出..它看起来不像我引用除视图以外的任何地方,我隐藏的CSRF字段被命名为“_csrf”..

 ./node_modules/express/node_modules/connect/lib/middleware/csrf.js: var secret = req.session._csrfSecret; ./node_modules/express/node_modules/connect/lib/middleware/csrf.js: req.session._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._csrf is deprecated, use req.csrfToken() instead'); ./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: <input type='hidden' name='_csrf' value={{csrf_token}} /> ./v/show.hbs: <input type='hidden' name='_csrf' value={{csrf_token}} /> 

这里是我试图POST到/ api / entries端点(我愚蠢地忽略提到这一点,但我使用connect-redis的会话中间件)时得到的整个错误堆栈:

 Error: Forbidden at Object.exports.error (appFolder/node_modules/express/node_modules/connect/lib/utils.js:63:13) at createToken (appFolder/node_modules/express/node_modules/connect/lib/middleware/csrf.js:82:55) at Object.handle (appFolder/node_modules/express/node_modules/connect/lib/middleware/csrf.js:48:24) at next (appFolder/node_modules/express/node_modules/connect/lib/proto.js:193:15) at next (appFolder/node_modules/express/node_modules/connect/lib/middleware/session.js:318:9) at appFolder/node_modules/express/node_modules/connect/lib/middleware/session.js:342:9 at appFolder/node_modules/connect-redis/lib/connect-redis.js:101:14 at try_callback (appFolder/node_modules/redis/index.js:580:9) at RedisClient.return_reply (appFolder/node_modules/redis/index.js:670:13) at ReplyParser.<anonymous> (appFolder/node_modules/redis/index.js:312:14) 

编辑2: connect-redis.js中的错误是一个试图通过会话ID获取当前会话并失败的函数。 不知道为什么会发生这种情况,我的connect-redis设置看起来是正确的。 这是杀了我

在最新版本的Express / Connect中,CSRF语法略有变化。 你现在希望你的中间件看起来像这样:

 .use(express.csrf()) .use(function (req, res, next) { res.cookie('XSRF-TOKEN', req.csrfToken()); res.locals.csrftoken = req.csrfToken(); next(); }) 

为了testing您的代码,请注意,您首先需要获取表单页面以生成CSRF令牌。 只有这样你的POST才能成功。 如果失败,则需要在重新尝试POST之前重新加载浏览器中的页面。

编辑 :如果你不需要file upload,不要使用multipart/form-data enctype。 切换到默认的enctype将允许express.csrf()parsing_csrf标记。

为了使用multipart/form-data enctypeparsing表单,您需要在应用程序configuration中使用多部分parsing器,或者自己处理file upload。 build议避免使用包含的express.bodyParser() ,而是使用诸如busboyformidable的路线,你期望file upload,以防止利用 。

如果你走这条路线,你的_csrf字段将不会被express.csrf()捕获,因为直到请求通过中间件之后,表单主体才会被parsing。 设置你的表单动作为'/api/entries?_csrf={{csrf_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(); }); }); }); 

我今天也遇到了这个问题,我花了几个小时find一个解决scheme。 希望这个答案可以帮助有我的确切问题的人。 作为@amagumori,我使用redis进行会话处理,并expression3.4.8,connect-redis 1.4.7。

基本上我能够确定我的快速configuration的顺序影响一个新的令牌发出的次数。 似乎所有被公开的东西都在创造一个新的标志。

特别是在我的情况下,我不得不移动电话

 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(); }); 

并按预期发布代币。