forbiddenError/403 Ajax Express Csurf - 如何正确使用Csurf?

forbiddenError/403 Ajax Express Csurf - how to use Csurf correctly?

嘿伙计们,我已经在这里待了一个星期了,但我不太明白。

我正在构建一个移相器游戏并使用 node 设置了一个记分牌,这是我第一次使用 node,我不太明白如何使用 csurf,因为文档太混乱了。

 ForbiddenError: invalid csrf token
    at verifytoken (/Users/jorybraun/web/highscore/node_modules/csurf/index.js:269:11)
    at csrf (/Users/jorybraun/web/highscore/node_modules/csurf/index.js:97:7)
    at Layer.handle [as handle_request] (/Users/jorybraun/web/highscore/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/jorybraun/web/highscore/node_modules/express/lib/router/index.js:312:13)
    at /Users/jorybraun/web/highscore/node_modules/express/lib/router/index.js:280:7
    at Function.process_params (/Users/jorybraun/web/highscore/node_modules/express/lib/router/index.js:330:12)
    at next (/Users/jorybraun/web/highscore/node_modules/express/lib/router/index.js:271:10)
    at /Users/jorybraun/web/highscore/app.js:43:5
    at Layer.handle [as handle_request] (/Users/jorybraun/web/highscore/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/jorybraun/web/highscore/node_modules/express/lib/router/index.js:312:13)

每次尝试从索引路由向我的路由评分路由发送 jquery post 请求时,我都会收到 403 错误。我真的很想限制对路线的访问,这样你就无法真正访问它们,除非你发出内部 ajax 请求。

这是我的 app.js 文件

// app.js
var mongodb      = require('mongodb');
var monk         = require('monk');
var credentials  = require('./credentials');
var db           = monk(credentials.uri);
var express      = require('express');
var path         = require('path');
var favicon      = require('serve-favicon');
var logger       = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser   = require('body-parser');
var csrf        = require('csurf');
var loadCsrf    = csrf({ cookie: true })
var parseForm   = bodyParser.urlencoded({ extended: false })


var scores       = require("./routes/scores");
var routes       = require('./routes/index'); 

var app          = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));


app.use(loadCsrf);

app.use('/', routes);
app.use('/scores', scores);

索引路线index.js

    var express = require('express');
var router = express.Router();

    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.render('index', { csrfToken: req.csrfToken() })
    });

得分路线scores.js

// scores.js 

var express = require('express');
var router = express.Router();


// GET scores, sorted by time
router.get('/', function(req, res) {
  console.log('GET scores');
  // Get the database object we attached to the request
  var db = req.db;
  // Get the collection
  var collection = db.get('scores');
  // Find all entries, sort by time (ascending)
  collection.find({}, { sort: { score : 1 } }, function (err, docs) {
    if (err) {
        // Handle error
        console.error('Failed to get scores', err);
        res.status(500).send('Failed to get scores');
    } else {
        // Respond with the JSON object
        res.json(docs);
    }
  });
});

// GET a number of top scores
// (the /top route without a number won't work unless we add it)
router.get("/top/:number", function(req, res) {
    console.log("GET top scores");
    // Read the request parameter
    var num = req.params.number;
    var db = req.db;
    var collection = db.get("scores");
    // Get all scores, but limit the number of results
    collection.find({}, { limit: num, sort: { score : 1 } }, function(err, docs) {
        if (err) {
            console.error("Failed to get scores", err);
            res.status(500).send("Failed to get scores");
        } else {
            res.json(docs);
        }
    });
});

// POST a score
router.post("/", function(req, res) {
    module.exports = router
    console.log("POST score");
    var name = req.body.name;
    var score = Number(req.body.score);
    var email = req.body.email;
    if (!(name && score)) {
        console.error("Data formatting error");
        res.status(400).send("Data formatting error");
        return;
    }
    var db = req.db;
    var collection = db.get("scores");    
    collection.insert({
        "name": name,
        "score": score,
        "email": email

    }, function(err, doc) {
        if (err) {
            console.error("DB write failed", err);
            res.status(500).send("DB write failed");
        } else {
            // Return the added score
            res.json(doc);
        }
    }); 
});


module.exports = router;

Ajax 在移相器中请求 canvas, 功能提交(){

    var name = prompt("Please enter your name");
    var email = prompt("Please enter your email adress, this will not be featured on the scoreboard");
    var csrf_token = "#{csrfToken}";

    $("body").bind("ajaxSend", function(elm, xhr, s){
      if (s.type == "POST") {
        xhr.setRequestHeader('X-CSRF-Token', csrf_token);
      }
    });


    $.post({
        url: 'http://localhost:3000/scores/',
        data: {

            name: name,
            email: email,
            score: score

        },
        success: function(data) {
            console.log('Score posted', data);
        },
        error: function(xhr, msg) {
            console.error('AJAX error', xhr.status, msg);
        }
    });

}

这会导致如下错误:

n.ajaxTransport.l.cors.a.crossDomain.send   @   jquery.min.js:4
n.extend.ajax   @   jquery.min.js:4
n.each.n.(anonymous function)   @   jquery.min.js:4
submit  @   game.js:263
c.SignalBinding.execute @   phaser.min.js:8
c.Signal.dispatch   @   phaser.min.js:8
c.Button.onInputUpHandler   @   phaser.min.js:12
c.SignalBinding.execute @   phaser.min.js:8
c.Signal.dispatch

我尝试了很多不同的选择,但如果有人能帮助我找到一个简单的解决方案,那就太棒了!

谢谢

好的,所以我弄清楚了为什么这不起作用 - 我浏览了大量的 Whosebugs 和教程,每个人都说要在 header 中设置它。我没有对此进行测试,但我认为您需要使用不同的 csrf 令牌才能让 head 工作。此标记设置在 body:

res.render('index', { csrfToken: req.csrfToken() })

它正在寻找以 _csrf 作为 body 中的密钥的密钥对值。目前正在运行:

    var csrf_Token = getCsrfToken();  
    function getCsrfToken() { 
      var metas = document.getElementsByTagName('meta'); 

        for (i=0; i<metas.length; i++) { 
          if (metas[i].getAttribute("name") == "_csrf") { 
             return metas[i].getAttribute("content"); 
          } 
        } 

        return "";
      } 


    $.post({
        url: 'http://localhost:3000/scores/',
        data: {

            _csrf: csrf_Token,
            name: name,
            email: email,
            score: score

        },
        success: function() {
            console.log('Score posted');
        },
        error: function(xhr, msg) {
            console.error('AJAX error', xhr.status, msg);
        }
    });

如果有人能帮我找到另一种方法来完成这项工作,我会很高兴,因为通过元标记将令牌传递给客户端似乎并不是执行此操作的最佳方法。