快速 CSRF 令牌验证

Posted

技术标签:

【中文标题】快速 CSRF 令牌验证【英文标题】:Express CSRF token validation 【发布时间】:2016-01-08 16:30:32 【问题描述】:

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

我想做的是为每个发布请求生成一个新令牌并检查它是否有效。一件事我知道为了安全我应该这样做,但我坚持了。

这是漫长的一天,我是 Express 和 NodeJS 的新手。

这是我当前的设置。

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');
);

send.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 Configurations working with Cookies。这对我有用。 不要将您的 CSRF 令牌放入 cookie。 【参考方案1】:

根据你分享的代码量,我会提到一些我觉得不太对劲的地方:

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 错误。我稍微编辑了这个问题,希望现在更清楚了。 我现在收到TypeError: req.csrfToken is not a function 错误。我们不应该在app.use(function) 部分之前使用app.use(csrf() 吗? @salep 你是对的! csrf() 应该在 app.use 部分之前 现在我回到无效的 csrf 令牌部分。每当我发布表单或刷新页面 (GET) 时,XCSF-TOKEN 都会发生变化,而 _csrf 是相同的。我认为它正在尝试获取 _csrf 的值,但我不确定。 @salep 也许你运行req.csrfToken() 的次数太多了。应该只有一个令牌生成【参考方案2】:

cookie 中的令牌将与 express 会话中的令牌完全不同。你想检查一个或另一个而不是两者。

我会完全禁用 cookie!因为它对我有用。

var csrfProtection = csurf( cookie: false );

作者在这里提到 https://github.com/expressjs/csurf/issues/52

接下来你想将“X-CSRF-Token”添加到此处找到的 ajax 帖子的标题中: Express.js csrf token with jQuery Ajax

【讨论】:

只要使用 express 就可以将 X-CSRF-Token 设置为 cookie、标头或局部变量,为什么还要使用 JQuery? OP 正在尝试根据表单帖子进行验证【参考方案3】:

下面的代码对我有用。如果您仍然遇到问题,请告诉我。

如前所述,您希望使用 cookie,您已让 csurf 知道您正在使用 cookie 来设置 CSRF 令牌。

第一步:配置

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

Step2:在路线中,

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>

【讨论】:

以上是关于快速 CSRF 令牌验证的主要内容,如果未能解决你的问题,请参考以下文章

如何生成和验证 csrf 令牌

api 端点未在 Sanctum 上进行 CSRF 令牌验证 - CSRF 令牌不匹配

设计用户登录为 CSRF 令牌真实性令牌提供身份验证错误

在 laravel 5.5 的验证 csrf 令牌中没有收到错误令牌不匹配异常

Symfony:身份验证请求失败:无效的 CSRF 令牌

警告:无法使用设计验证 CSRF 令牌的真实性