Express CSRF令牌验证

10

我遇到了CSRF令牌的问题。当我提交表单时,会生成一个新的XSRF-TOKEN,但我认为我生成了两个不同的令牌,我有点困惑。还有一个名为_csrf的令牌,在开发工具中看到了两个不同的cookie(XSRF-TOKEN和_csrf)。在提交后,_csrf不会改变。

我想做的是为每个提交请求生成一个新的令牌并检查其是否有效。我知道这样做是为了安全起见,但我卡住了。

这是我的当前设置。

var express = require('express')
  , passport = require('passport')
  , flash = require('connect-flash')
  , utils = require('./utils')
  , csrf = require('csurf')
  // setup route middlewares
  ,csrfProtection = csrf({ cookie: true })
  , methodOverride = require('method-override')
  , bodyParser = require("body-parser")
  , parseForm = bodyParser.urlencoded({ extended: false })
  , cookieParser = require('cookie-parser')
  , cookieSession = require('cookie-session')
  , LocalStrategy = require('passport-local').Strategy
  , RememberMeStrategy = require('../..').Strategy;


var app = express();

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.logger());
app.use(express.static(__dirname + '/../../public'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(express.session({ secret: 'keyboard cat' }));
app.use(flash());
// Initialize Passport!  Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.authenticate('remember-me'));
app.use(app.router);
app.use(csrf());

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

路由

app.get('/form', csrfProtection, function(req, res) {
  // pass the csrfToken to the view
  res.render('send', { csrfToken: req.csrfToken()});
});

app.post('/process', parseForm, csrfProtection, function(req, res) {
  res.send('data is being processed');
});

发送.ejs (/form GET)

<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">

  Favorite color: <input type="text" name="favoriteColor">
  <button type="submit">Submit</button>
</form>

请参考:CSRF配置与Cookies一起工作。这对我来说是有效的。 - Laxmeesh Joshi
2
不要将您的CSRF令牌放入cookie中。 - kta
4个回答

16

根据您分享的代码量,我会提到一些看起来不太对的事情:

1. 您可能需要交换下面的行,以便在路由之前运行csrf。

app.use(csrf());
app.use(app.router);

2. 在路由之前也需要放置csrftoken设置。

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

3. 在您的表单中需要使用locals.csrftoken

<form action="/process" method="POST">
  <input type="hidden" name="_csrf" value="<%= csrftoken %>">

  Favorite color: <input type="text" name="favoriteColor">
  <button type="submit">Submit</button>
</form>

已经完成了这个,现在出现了“ForbiddenError: invalid csrf token”的错误。我稍微编辑了一下问题,希望现在更清晰了。 - salep
现在我得到了TypeError: req.csrfToken不是一个函数的错误。难道我们不应该在app.use(function)部分之前使用app.use(csrf()吗? - salep
1
@salep 你是对的!csrf() 应该在 app.use 部分之前。 - filype
现在我回到了无效的csrf令牌部分。每当我提交表单或刷新页面(GET)时,XCSF-TOKEN都会更改,而_csrf则保持不变。我认为它正在尝试获取_csrf的值,但我不确定。 - salep
@salep 也许你运行了太多次 req.csrfToken()。应该只生成一个令牌。 - filype
不要运行req.csrfToken()两次...app.use(function (req, res, next) { var csrfToken = req.csrfToken(); res.cookie('XSRF-TOKEN', csrfToken); res.locals.csrftoken = csrfToken; next(); }); - buycanna.io

0

1
为什么要使用JQuery,当你可以通过使用Express将X-CSRF-Token设置为cookie、头文件或本地变量呢? - Sherwin Ablaña Dapito
OP 正在尝试根据表单提交进行验证。 - Michael P.

0
以下代码对我有效。如果您仍然遇到问题,请告诉我。
如您所述,您希望使用cookies,因此您需要让csurf知道您正在使用cookies来设置CSRF令牌。
步骤1:配置
var csrf = require('csurf');
var cookieparser= require('cookie-parser'); 

//cookieparser must be placed before csrf 
app.use(bodyparser.urlencoded({extended:false}));
app.use(cookieParser('randomStringisHere222'));
app.use(csrf({cookie:{key:XSRF-TOKEN,path:'/'}}));

//add the your app routes here
app.use("/api", person);
app.use("/", home);

步骤2:在路由中,

res.render('myViewPage',{csrfTokenFromServer:req.csrfToken()}); 

步骤三:在 HTML 中包含一个隐藏字段以用于 CSRF 令牌。例如:
<form action="/api/person" method="POST">
      <input type="hidden" name="_csrf" value=<%=csrfTokenFromServer %> />
      First name:<br>
      <input type="text" name="firstname" value="">
      <br>
      Last name:<br>
      <input type="text" name="lastname" value="">
      <br><br>
      <input type="submit" value="Submit">
 </form>

0

当我们设置 csrf cookie 时,它具有默认键 _csrf。我们可以覆盖它。所以在我的情况下,我给了这个 cookie 相同的名称。

const csrf = csurf({cookie:{key:'XSRF-TOKEN'}});

app.get('/csrf-token', csrf, (req: Request, res: Response, next: NextFunction) => {
  const newToken = req.csrfToken();

  res.cookie('XSRF-TOKEN', newToken, {
    httpOnly: true,
    sameSite: 'strict',
    secure: process.env.NODE_ENV === 'production',
  });

  res.json({ csrfToken: newToken });
});

我不知道你是否已经解决了这个问题,但如果有其他人正在寻找解决方法,这仍然会有所帮助。


这是一个非常糟糕的做法,仅创建一个API返回CSRF令牌作为响应。 - Keyur Chavda-kc1994

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接