Node Express y csurf – 403 (Prohibido) token csrf no válido

Revisé e intenté todo lo que pude encontrar aquí, y en otros lugares con Google … y no puedo superar esto. Estoy usando Node, Express, EJS y estoy intentando usar csurf en un formulario, que se publica con jjay ajax. No importa cómo configure csurf, obtengo “403 (Prohibido) token csrf no válido”

He intentado configurar tanto globalmente en app.js como en el controlador. Esto es lo que intenté en app.js:

var express = require('express'); var session = require('express-session'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var mysql = require('mysql'); var flash = require("connect-flash"); var csrf = require("csurf"); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.use(logger('dev')); app.use(cookieParser()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(session({ secret: 'somethingsecret', resave: true, saveUninitialized: true, httpOnly: true, secure: false })); app.use(csrf()); app.use(function (req, res, next) { var token = req.csrfToken(); res.cookie('XSRF-TOKEN', token); res.locals.csrfToken = token; console.log("csrf token = " + token); next(); }); app.use(flash()); app.use(express.static(path.join(__dirname, 'public'))); app.use(function (err, req, res, next) { if (err.code !== 'EBADCSRFTOKEN') return next(err); // handle CSRF token errors here res.status(403); res.send('form tampered with'); }) //routing var routes = require('./routes/index'); var users = require('./routes/users'); var register = require('./routes/register'); app.use('/', routes); app.use('/users', users); app.use('/register', register); 

… con este controlador:

 var express = require("express"); var router = express.Router(); var bodyParser = require("body-parser"); var userSvc = require("../service/userservice"); var jsonParser = bodyParser.json(); router.get("/", function(req, res, next) { console.log("token = " + token); userSvc.getAllPublicRoles(function(data) { res.render("register", { title: "Register a new account", roles: data }); }); }); router.post("/new", jsonParser, function(req, res, next) { userSvc.addUser(req.body, function(result) { console.log("New user id = " + result.insertId); res.send('{"success" : "Updated Successfully", "status" : 200}'); }); }); 

… y esta vista:

formar:

  <input type="hidden" name="_csrf" value="" /> 

llamada ajax:

  $.ajax({ url: "/register/new", type: "POST", dataType: "json", data: user }).done(function(data) { if (data) { console.log("Success! = " + data); } }).fail(function(data) { console.log("Something went wrong: " + data.responseText); }); 

Luego simplemente intenté hacer todo lo que estaba en el controlador, eliminando todas las referencias, llamadas, etc. de app.js, y usando la misma forma y llamada ajax que arriba:

 var express = require("express"); var router = express.Router(); var bodyParser = require("body-parser"); var csrf = require("csurf"); var userSvc = require("../service/userservice"); var csrfProtection = csrf(); var jsonParser = bodyParser.json(); router.get("/", csrfProtection, function(req, res, next) { var token = req.csrfToken(); console.log("token = " + token); userSvc.getAllPublicRoles(function(data) { res.render("register", { title: "Register a new account", csrfToken: token, roles: data }); }); }); router.post("/new", jsonParser, csrfProtection, function(req, res, next) { userSvc.addUser(req.body, function(result) { console.log("New user id = " + result.insertId); res.send('{"success" : "Updated Successfully", "status" : 200}'); }); }); 

No estoy seguro de a dónde ir desde aquí. He estado usando el nodo durante aproximadamente dos semanas, en mi tiempo libre, así que perdona mi ignorancia aquí.

Si desea almacenar el token en una cookie en lugar de la sesión, deje que csurf cree la cookie para usted, por ejemplo.

 // Store the token in a cookie called '_csrf' app.use(csrf({cookie: true)); // Make the token available to all views app.use(function (req, res, next){ res.locals._csrf = req.csrfToken(); next(); }); 

Luego, debe asegurarse de que el token esté disponible cuando realice la llamada utilizando AJAX a través de los datos de POST, o como un encabezado de solicitud personalizado como ‘xsrf-token’.

En el momento, está proporcionando el token al formulario, pero no la solicitud real (enviada mediante AJAX).

Por ejemplo, podría representar el token en la configuración de AJAX:

 $.ajaxSetup({ headers: {"X-CSRF-Token": "{{csrfToken}}" } }); 

Después de varias horas más de resolución de problemas y búsqueda, encontré una publicación que ayudó a responderla. Todo lo que necesitaba era pasar el valor del encabezado en la publicación ajax. Tiene sentido, simplemente lo pasé por alto. Al igual que:

  

… y luego en jQuery:

  $.ajaxSetup({ headers: {"X-CSRF-Token": $("#_csrf").val()} }); 

además de agregar el “X-CSRF-Token” al encabezado de la publicación, usted desea deshabilitar las cookies por completo.

var csrfProtection = csurf ({cookie: false});

el autor lo menciona aquí https://github.com/expressjs/csurf/issues/52

la validación de cookies y sesiones no debe combinarse, aunque es un poco engañosa, ya que ha combinado la validación de cookies y sesiones en su documentación: https://github.com/expressjs/csurf#simple-express-example

Otro enfoque sobre mi proyecto personal es reenviar un token nuevo cuando presento mi formulario con éxito:

Por ejemplo, sobre mi formulario (que carga archivos) tengo el siguiente html:

 

Y en el cambio de archivo, desencadeno la carga así

  $('#excell_upload').on('change',function(event){ event.preventDefault(); var formData = new FormData($("#upload_form")[0]); $.ajax({ 'type':$("#upload_form").attr('method'), 'data': formData, 'url': $("#upload_form").attr('action'), 'processData': false, 'contentType': false, 'mimeType': 'multipart/form-data', 'headers': {"X-CSRF-Token": $("#upload_form").attr('data-csrf') }, 'beforeSend': function (x) { if (x && x.overrideMimeType) { x.overrideMimeType("multipart/form-data"); } $('#trigger_upload').addClass('disabled'); }, 'success':function(data){ $('#upload_form').attr('data-csrf',data.csrfToken) }, 'fail':function(){ }, 'complete':function(){ $('#trigger_upload').removeClass('disabled'); } }); }); 

Como notará, recibo un nuevo token csrf para poder reutilizar mi formulario para nuevos envíos. Regenero el token CSRF así:

 app.post('/data_assets',function(req,res,next){ res.json({'csrfToken':req.csrfToken()}); });