具有React App csurf的csrf无效令牌nodejs

时间:2020-05-13 07:34:49

标签: node.js reactjs csrf x-xsrf-token

我正在使用带有react前端的nodejs express API。我正在尝试设置csurf xsrf,但是它会继续返回invalid csrf token。我已经读过有关csrf的文章,并了解它正在解决的问题。

因此,基本上,每次我的react应用程序每次从服务器发出请求时,都会请求一个新令牌。 (我不是很肯定这是正确的。是否只需要在准备发出请求时才请求令牌?)

前端:localhost:3016 后端:localhost:3050

有人知道我在做什么错吗?

这是nodejs app.js文件

// Handler
const Entry = require("./handlers/entry");
const Test = require("./handlers/test");

const {Config,Environments} = require("./common/config");
const Auth = require("./funcs/user/auth");
const ObjectUtil = require("./utils/object");
const AccountStatusEnum = require("./enums/account-status").accountStatusEnum;

// Simulate real API calls
const delayResponse = 250;// milliseconds

// ==============================================
// Base setup
// ==============================================

process.env.TZ = "Etc/GMT"

const express     = require('express');
const app         = express();
const port        = 3050;
const bodyParser  = require('body-parser');
const cookieParser = require('cookie-parser');

const csrf = require('csurf');
const csrfProtection = csrf({ 
    cookie: true, 
    path: "/login",
    key: "x-csrf-token",
    domain: Config.FrontEnd.Url,
  });

app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(csrfProtection)
app.use(require('express-useragent').express());

// Cors
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, auth-id, auth-token, x-csrf-token, _csrf");
  res.header('Access-Control-Allow-Methods', 'PATCH, POST, GET, DELETE, OPTIONS');
  next();
});

// csrf
app.use(function (err, req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, auth-id, auth-token, x-csrf-token, _csrf");
  res.header('Access-Control-Allow-Methods', 'PATCH, POST, GET, DELETE, OPTIONS');
  // handle CSRF token errors here
  if(err.code === 'EBADCSRFTOKEN'){
    let foo = req.headers['x-csrf-token'];

    res.status(403)
    let obj = {
      err: {
        code:1,
        message:err.message,
        headerCSRF: foo,
      },
      res: {},
    };
    response(req,res,obj);
    return;
  }
  next();

})

// ==============================================
// Auth
// ==============================================

const normRoute = (req, res, next) => {

  req.body = (req.body != undefined && req.body != null) ? ObjectUtil.toCamelCaseKeys(req.body) : req.body;
  response(req,res,next(req));
}

const authRoute = async (req, res, next, minimumStatus) => {
  req.body = (req.body != undefined && req.body != null) ? ObjectUtil.toCamelCaseKeys(req.body) : req.body;

  let authId = (req.headers['auth-id'] !== undefined) ? req.headers['auth-id'] :"";
  let authToken = (req.headers['auth-token'] !== undefined) ? req.headers['auth-token'] : "";

  const r = await Auth.authUser(
            req.ip,
            req.useragent,
            authId,
            authToken,
            minimumStatus,
          );
  if(r.err.code){ 
    response(req,res,r); 
    return false; 
  }  

  response(req,res,next(req,r.res.user));

}

// ==============================================
// Routes
// ==============================================

app.get('/', function(req, res) {
  res.send('<html><head></head><body>API is running<br> Check the docs for the details</body></html>');  
});

app.post('/login', function(req,res) { normRoute(req,res,Entry.postLogin) });
app.post('/logout', function(req,res) { authRoute(req,res,Entry.postLogout,AccountStatusEnum.banned) });

app.get('/test', function(req,res) { normRoute(req,res,Test.get) });
app.post('/test', function(req,res) { authRoute(req,res,Test.post,AccountStatusEnum.basic) });

// ==============================================
// Response type
// ==============================================

const response = async (req,res,obj) => { 
  await obj;

  Promise.resolve(obj).then(function(val) {
    if(delayResponse >= 1 && (Config.Env === Environments.Local)){
      setTimeout(function(){  
        resume(req,res,val);
      }, delayResponse);
      return true;
    } 
    resume(req,res,val);
  });
}

const resume = (req,res,obj) => {

  obj = (obj === undefined) ? {} : obj;

  var status = (obj.status !== undefined) ? obj.status : 200;
  // Let status override settings
  if(obj.status === undefined){
    if((obj.err.code)){
      status = 400;
    }else if(obj.res === undefined || ObjectUtil.isEmpty(obj.res)){
      // status = 204;
    }
  }

  let json = {};
  json.err = obj.err;
  json.res = obj.res;

  let csrfToken = req.csrfToken();
  json.csrf = csrfToken;
  console.log("json.csrf",json.csrf);
  res.cookie('x-csrf-token', csrfToken);

  json = ObjectUtil.toCamelCaseKeys(json);
  res.status(status).json(json);

}

// ==============================================
// Start the server
// ==============================================
app.listen(process.env.PORT || port);
console.log('Magic happens on port ' + port);

前端将其作为标头发送

// has both in here for testing atm
headers['_csrf'] = (localStorage.getItem("csrf") !== null) ? localStorage.getItem("csrf"): "";
headers['x-csrf-token'] = (localStorage.getItem("csrf") !== null) ? localStorage.getItem("csrf"): "";

这是网络信息

General
Request URL: http://localhost:3050/login
Request Method: POST
Status Code: 400 Bad Request
Remote Address: [::1]:3050
Referrer Policy: no-referrer-when-downgrade

Response
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, auth-id, auth-token, x-csrf-token, _csrf
Access-Control-Allow-Methods: PATCH, POST, GET, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 156
Content-Type: application/json; charset=utf-8
Date: Wed, 13 May 2020 07:17:21 GMT
ETag: W/"9c-PlmNfMbUDSIMeWXLZoHk3lyBG/4"
Set-Cookie: _csrf=ZrkXaXMiYQfyIVaALbG9lJaX; Path=/
Set-Cookie: x-csrf-token=Y603zmAK-wBbU8LMDILCMz4VaodZBdAYfOS4; Path=/
X-Powered-By: Express

Request Headers
_csrf: wPQqioyo-D47QGH0F1fytuUG2JXP2F7uDmFM
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,ja;q=0.8
Connection: keep-alive
Content-Length: 39
Content-Type: application/json
Host: localhost:3050
Origin: http://localhost:3016
Referer: http://localhost:3016/login
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
x-csrf-token: wPQqioyo-D47QGH0F1fytuUG2JXP2F7uDmFM

0 个答案:

没有答案