"Invalid csrf token" 在节点 js(Express) 中使用 CSRF。CSRF 令牌对第一个请求工作正常,但对所有其他请求给出错误

"Invalid csrf token" using CSURF in nodejs(Express).CSRF token works fine for first request but give error for all other requests

我正在使用 NodeJS Express 和 passport.js 进行用户身份验证。我在我的登录表单中实现了 csrf 身份验证。当我第一次进入登录页面时,Csrf 令牌工作正常,但是当我注销并重定向到登录页面时,我收到错误 "Invalid csrf token".

我已经尝试使用 res.render({csrf: req.csrfToken()}); 将 csrf 令牌显式传递给视图(EJS 模板引擎);但是没用。

const path = require('path');
const sequalize = require('./utils/database');
const localStrategy = require('passport-local').Strategy;
//const User = require('../models/user');
const bycrypt = require('bcryptjs');

const express = require('express');
const session = require('express-session');
const sessionStore = require('express-mysql-session')(session);
const passport = require('passport');
const bodyParser = require('body-parser');
const csrf = require('csurf');
const flash = require('connect-flash');
const User = require('./models/user');

const app = express();

var options = {
    host: 'localhost',
    port: 3306,
    user: 'root',
    password: '',
    database: 'lab'
};

var mysqlStore = new sessionStore(options);

app.set('view engine', 'ejs');
app.set('views', 'views');
app.use(flash());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
    key: 'session_cookie_name',
    secret: 'session_cookie_secret',
    store: mysqlStore,
    resave: false,
    saveUninitialized: false
}));

app.use(passport.initialize());
app.use(passport.session());

app.use(csrf());
app.use(flash());
app.use((req, res, next) => {
    //res.locals.isAuthenticated = req.session.isLoggedIn;
    passport.serializeUser(function(user, done) {
        done(null, user);
    });

    passport.deserializeUser(function(user, done) {
        done(null, user);
    });

    passport.use(new localStrategy((username, password, done) => {
        //console.log(username);
        User.findOne({ email: username })
            .then(user => {
                if (!user) {
                    req.flash('err', 'Invalid email or password.');
                    done(null, false);
                }
                bycrypt
                    .compare(password, user.password)
                    .then(doMatch => {
                        if (doMatch) {
                            // req.session.isLoggedIn = true;
                            // req.session.user = user;
                            //console.log('Success');
                            done(null, user);
                        } else {
                            req.flash('err', 'Invalid email or password.');
                            //console.log('not logged in');
                            done(null, false);
                        }
                    })
                    .catch(err => {
                        req.flash('err', 'Something did not go well.');
                        //console.log('not logged in');
                        done(null, false);
                    });
            });
    }));
    res.locals.csrfToken = req.csrfToken();
    next();
});
app.use('/', adminRoute);
sequalize
.sync()
.then(() => {
        app.listen(3000);
    })
    .catch(err => {
        console.log(err);
    });

注销路由代码

req.logOut();
    res.render('auth/login', {
        flashError: req.flash('err')
    });

HTML注销按钮代码

<form id="my_form" method="post" action="/logout">
      <button onclick="document.getElementById('my_form').submit();"><i class="fa fa-power-off" style="color:#E27D60"><span> <b> Logout</b></span></i></button>
</form>

这是我在注销后使用 req.logout() 呈现登录页面时遇到的错误。

ForbiddenError: invalid csrf token at csrf (K:\Node LAB\node_modules\csurf\index.js:112:19) at Layer.handle [as handle_request] (K:\Node LAB\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (K:\Node LAB\node_modules\express\lib\router\index.js:317:13) at K:\Node LAB\node_modules\express\lib\router\index.js:284:7 at Function.process_params (K:\Node LAB\node_modules\express\lib\router\index.js:335:12) at next (K:\Node LAB\node_modules\express\lib\router\index.js:275:10) at SessionStrategy.strategy.pass (K:\Node LAB\node_modules\passport\lib\middleware\authenticate.js:338:9) at K:\Node LAB\node_modules\passport\lib\strategies\session.js:69:12 at pass (K:\Node LAB\node_modules\passport\lib\authenticator.js:337:31) at deserialized (K:\Node LAB\node_modules\passport\lib\authenticator.js:349:7) at K:\Node LAB\app.js:140:9 at pass (K:\Node LAB\node_modules\passport\lib\authenticator.js:357:9) at Authenticator.deserializeUser (K:\Node LAB\node_modules\passport\lib\authenticator.js:362:5) at SessionStrategy.authenticate (K:\Node LAB\node_modules\passport\lib\strategies\session.js:60:10) at attempt (K:\Node LAB\node_modules\passport\lib\middleware\authenticate.js:361:16) at authenticate (K:\Node LAB\node_modules\passport\lib\middleware\authenticate.js:362:7) at Layer.handle [as handle_request] (K:\Node LAB\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (K:\Node LAB\node_modules\express\lib\router\index.js:317:13) at K:\Node LAB\node_modules\express\lib\router\index.js:284:7 at Function.process_params (K:\Node LAB\node_modules\express\lib\router\index.js:335:12) at next (K:\Node LAB\node_modules\express\lib\router\index.js:275:10) at initialize (K:\Node LAB\node_modules\passport\lib\middleware\initialize.js:53:5)

终于知道问题出在哪里了。问题是我忘记在我的注销表单中添加 csrf 令牌的隐藏字段,因为 CSRF 身份验证需要每个表单都有这个字段。

我之前的注销按钮代码:

<form id="my_form" method="post" action="/logout">
    <button onclick="document.getElementById('my_form').submit();"><i class="fa fa-power-off" style="color:#E27D60"><span> <b> Logout</b></span></i></button>
</form>

现在我更正为:

<form id="my_form" method="post" action="/logout">
     <input type="hidden" name="_csrf" value="<%= csrfToken %>">
     <button onclick="document.getElementById('my_form').submit();"><i class="fa fa-power-off" style="color:#E27D60"><span> <b> Logout</b></span></i></button>
</form>

在登录表单中,已包含隐藏的 csrf 字段,这就是它工作正常的原因