【问题标题】:NodeJS csrf protection gives ForbiddenError invalid csrf token only on one POST request formNodeJS csrf 保护仅在一个 POST 请求表单上提供 ForbiddenError 无效 csrf 令牌
【发布时间】:2020-02-19 02:55:27
【问题描述】:

在过去的几个小时里,我一直未能成功地在 Google 上找到以下问题的解决方案:

我已经设置好 csurf 并且运行良好。它适用于与注册/注册用户相关的 POST 请求。我正在尝试在用户配置文件中创建一个更新用户数据的表单,但是当我点击提交时,我得到ForbiddenError: invalid csrf token

在进一步测试中,在个人资料页面上创建了 csrt 令牌,但由于某种原因,它是无效的。

(我最近才开始学习编程,如果我错过了什么,我愿意提供更多信息。)

使用的工具:

  • Node.js v10.16.3
  • Express v4.17.1
  • ejs v2.7.1
  • csurf v1.10.0
  • Express 会话 v1.16.2
  • 护照 v^0.4.0
  • cookie 解析器 1.4.4
  • 正文解析器 v1.19.0

我的 app.js

let express = require("express"),
app = express(),
bodyParser = require("body-parser"),
cookieParser = require('cookie-parser'),
session = require("express-session"),
mongoose = require("mongoose"),
passport = require("passport"),
flash = require('connect-flash'),
validator = require('express-validator'),
LocalStrategy = require("passport-local"),
csrf = require('csurf'),
csrfProtection = csrf({ cookie: true }),
MongoStore = require('connect-mongo')(session);

let indexRoutes = require('./routes/index');
let userRoutes = require('./routes/user');
let User = require("./models/user");

// APP CONFIGURATION
mongoose.connect("mongodb://localhost:27017/azax", { useNewUrlParser: true, useUnifiedTopology: true, }).then(() => {
    console.log("Connected to MongoDB");
}).catch((error) => {
    console.log("Something is wrong...");
});


require('./config/passport');

// View engine setup
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/public"));

// Initial setup
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(validator());

// Session setup
app.use(cookieParser());
app.use(session({
    secret: 'somesecretforbytox',
    resave: false,
    saveUninitialized: false
}));
app.use(flash());

// Initialize passport
app.use(passport.initialize());
app.use(passport.session());
app.use(csrf());

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

app.use(function (req, res, next) {
    res.locals.currentUser = req.user;
    res.locals.session = req.session;
    next();
});

// ======================
// Connect to route files
// ======================
app.use('/user', userRoutes);
app.use(indexRoutes);

app.listen(3033, function () {
    console.log("Listening at port 3033...");
});

我的护照.js:

let passport = require('passport');
let User = require('../models/user');
let LocalStrategy = require('passport-local').Strategy;

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

passport.deserializeUser(function (id, done) {
  User.findById(id, function (err, user) {
    done(err, user);
  });
});

passport.use('local-signup', new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: true
}, function (req, email, password, done) {
  req.checkBody('username', 'Invalid username').notEmpty();
  req.checkBody('email', 'Invalid email').notEmpty().isEmail();
  req.checkBody('password', 'Invalid password').notEmpty().isLength({ min: 4 });
  let errors = req.validationErrors();
  if (errors) {
    let messages = [];
    errors.forEach(function (error) {
      messages.push(error.msg);
    });
    return done(null, false, req.flash('error', messages));
  }
  User.findOne({ 'email': email }, function (err, user) {
    if (err) {
      return done(err);
    }
    if (user) {
      return done(null, false, { message: 'Вече има акаунт с този имейл.' })
    }
    let username = req.body.username;
    let newUser = new User();
    newUser.username = username;
    newUser.email = email;
    newUser.password = newUser.encryptPassword(password);
    newUser.country = 'България';
    newUser.save(function (err, result) {
      if (err) {
        return done(err);
      }
      return done(null, newUser);
    });
  });
}));

passport.use('local-signin', new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: true
}, function (req, email, password, done) {
  // req.checkBody('username', 'Invalid username').notEmpty();
  req.checkBody('email', 'Invalid email').notEmpty();
  req.checkBody('password', 'Invalid password').notEmpty();
  let errors = req.validationErrors();
  if (errors) {
    let messages = [];
    errors.forEach(function (error) {
      messages.push(error.msg);
    });
    return done(null, false, req.flash('error', messages));
  }
  User.findOne({ 'email': email }, function (err, user) {
    if (err) {
      return done(err);
    }
    if (!user) {
      return done(null, false, { message: 'Акаунтът не е намерен.' });
    }
    if (!user.validPassword(password)) {
      return done(null, false, { message: 'Грешна парола.' });
    }
    return done(null, user);
  });
}));

HTML...:

<form action="/user/profile" method="POST" class="form-validate form-horizontal" enctype="multipart/form-data">
    <fieldset>
        <legend>Edit address</legend>
        <!-- email -->
        <div class="control-group">
            <div class="control-label">
                <label id="jform_email1-lbl" for="jform_email" class="hasPopover required" title="" data-content="Enter new email address." data-original-title="Email Address">
                    Email<span class="star">&nbsp;*</span></label>
            </div>
            <div class="controls">
                <input type="email" name="email" class="validate-email required" id="jform_email" value="<%= (typeof currentUser.email != 'undefined' ? currentUser.email : '') %>" size="30" autocomplete="email" required aria-required="true">
            </div>
        </div>
        <!-- name -->
        <div class="control-group">
            <div class="control-label">
                <label id="jform_fname-lbl" for="jform_fname" class="hasPopover required" title="" data-content="Enter new name." data-original-title="Name">
                    Name<span class="star">&nbsp;*</span></label>
            </div>
            <div class="controls">
                <input type="text" name="firstName" id="jform_fname" value="<%= (typeof currentUser.firstName != 'undefined' ? currentUser.firstName : '') %>" class="required" size="30" required aria-required="true">
            </div>
        </div>
    </fieldset>

    <div class="form-actions">
        <input type="hidden" name="_csrf" value="<%= csrfToken %>">
        <button type="submit" class="btn btn-primary validate">
            <span>Save</span>
        </button>
        <a class="btn" href="/" title="Cancel">Cancel</a>
    </div>
</form>

user.js:

let express = require('express'),
    router = express.Router(),
    csrf = require('csurf'),
    csrfProtection = csrf(),
    passport = require('passport');

router.use(csrfProtection);
let User = require("../models/user");

// user profile
router.get("/profile", isLoggedIn, csrfProtection, function (req, res) {
    res.render("user/profile", { csrfToken: req.csrfToken(), currentUser: req.user });
});

router.post('/profile', (req, res) => {
    updateRecord(req, res);
    res.redirect('/profile');
});
// update user data
function updateRecord(req, res) {
    User.findOne({ _id: req.user.id }, (err, doc) => {
        doc.name = req.body.name;
        doc.save(function (err, doc) {
        });
    });
}
router.get("/profile/edit", isLoggedIn, csrfProtection, function (req, res) {
     res.render("user/edit", { csrfToken: req.csrfToken(), currentUser: req.user });
});

// sign up form works with csrf

// signup form
router.get("/signup", csrfProtection, function (req, res) {
    let messages = req.flash('error');
    res.render("user/signup", { csrfToken: req.csrfToken(), messages: messages, hasErrors: messages.length > 0 });
});

// ... more routes

    module.exports = router;

// middleware
function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect('/user/login');
}

【问题讨论】:

    标签: node.js express passport.js csrf ejs


    【解决方案1】:

    对我有用的是在每个表单中添加以下隐藏字段:

    <input type="hidden" name="_csrf" value="<%= csrfToken %>">
    

    并将令牌发送到显示表单的模板页面的每个获取请求。

    {csrfToken: req.csrfToken()}
    

    喜欢

    router.get('/user/login',function(req,res){
        res.render('pages/login', {csrfToken: req.csrfToken()});
    })
    

    这修复了整个应用程序中的错误。

    【讨论】:

      猜你喜欢
      • 2015-10-31
      • 2016-08-21
      • 2021-04-11
      • 2012-11-04
      • 2018-11-03
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多