Защита NodeJS csrf выдает ForbiddenError неверный токен csrf только в одной форме запроса POST - PullRequest
0 голосов
/ 23 октября 2019

Последние несколько часов я безуспешно пытался найти решение в Google для следующей проблемы:

У меня настроен csurf и он работает хорошо. Это работает для запросов POST, связанных с регистрацией / в пользователях. Я пытаюсь создать форму в профиле пользователя, которая обновляет данные пользователя, но когда я нажимаю кнопку отправить, я получаю ForbiddenError: invalid csrf token.

При дальнейшем тестировании токен csrt создается на странице профиля, но по какой-то причине он недействителен.

(Я только недавно занялся программированием, хочу предоставитьдополнительная информация, если я что-то пропустил.)

Использованные инструменты:

  • Node.js v10.16.3
  • Express v4.17.1
  • ejsv2.7.1
  • csurf v1.10.0
  • Экспресс-сессия v1.16.2
  • Паспорт v ^ 0.4.0
  • парсер cookie 1.4.4
  • body-parser v1.19.0

My 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...");
});

My passport.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');
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...