parse-community / parse-server

Parse Server for Node.js / Express
https://parseplatform.org
Apache License 2.0
20.92k stars 4.78k forks source link

Getting CSRF issue when executing Parse.Query().find() in Express endpoint #1972

Closed woodardj closed 8 years ago

woodardj commented 8 years ago

I've got a set of routes that were previously served up by Cloud Code, but wrap the Parse calls in a REST API that I use to publicly pull data for a website and other places where I'd rather not have Parse credentials in javascript.

I'm requiring the file with all of these endpoints after the

var api = new ParseServer({
  databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
  cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
  appId: process.env.APP_ID || 'myAppId',
  masterKey: process.env.MASTER_KEY || 'masterKey', 
  serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',  
  liveQuery: {
    classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
  }
});

var app = express();

app.use('/', express.static(path.join(__dirname, '/public'))); // TODO: Potentially change to /assets (or other) and refactor all views

// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || "/parse";
app.use(mountPath, api);

require ('./cloud/app.js')(app);

cloud/app.js looks like:

module.exports = function(app) {
...
  var csrf = require('csurf');
  app.use(csrf({cookie: true}));
...
// PUBLIC API - WEBSITE
app.get('/public/api/recent', function (req, res) {
  console.log("we're inside the callback");
    // this route is called from the public website
    // therefore, no parameters are allowed...

    // Parse.Cloud.useMasterKey();

    var trailingMinsBegin = 300;
    var trailingMinsEnd = 0;

    var createdStart = moment().subtract(trailingMinsBegin, 'minutes').toDate();
    var createdEnd = moment().subtract(trailingMinsEnd, 'minutes').toDate();

    Parse.Promise.as()

        .then(function () {
            var q1 = new Parse.Query(models.MyModel);
            q1.greaterThanOrEqualTo('createdAt', createdStart);
            q1.lessThan('createdAt', createdEnd);
            q1.select('createdAt', 'giverDisplayName', 'recipientDisplayName', 'affiliate');
            q1.include('affiliate');
            q1.descending('createdAt');
            q1.limit(50);

            var q2 = new Parse.Query(models.MyOtherModel);
            q2.greaterThanOrEqualTo('createdAt', createdStart);
            q2.lessThan('createdAt', createdEnd);
            q2.equalTo('isCurrentPR', true);
            q2.equalTo('libraryType', constants.MyLibraryType.Designation);
            q2.select('createdAt', 'userDisplayName', 'title', 'affiliate');
            q2.include('affiliate');
            q2.descending('createdAt');
            q2.limit(50);

            console.log(q1);
            console.log(q2);
            return Parse.Promise.when([q1.find({useMasterKey: true}), q2.find({useMasterKey: true})]);
        })
...

When calling up this endpoint via GET, in, say, a browser, I end up with two csrf errors in the server log:

ForbiddenError: invalid csrf token
    at verifytoken (/app/node_modules/csurf/index.js:269:11)
    at csrf (/app/node_modules/csurf/index.js:97:7)
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:82:5)
    at trim_prefix (/app/node_modules/express/lib/router/index.js:302:13)
    at /app/node_modules/express/lib/router/index.js:270:7
    at Function.proto.process_params (/app/node_modules/express/lib/router/index.js:321:12)
    at next (/app/node_modules/express/lib/router/index.js:261:10)
    at /app/cloud/app.js:150:9
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:82:5)
    at trim_prefix (/app/node_modules/express/lib/router/index.js:302:13)
ForbiddenError: invalid csrf token
    at verifytoken (/app/node_modules/csurf/index.js:269:11)
    at csrf (/app/node_modules/csurf/index.js:97:7)
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:82:5)
    at trim_prefix (/app/node_modules/express/lib/router/index.js:302:13)
    at /app/node_modules/express/lib/router/index.js:270:7
    at Function.proto.process_params (/app/node_modules/express/lib/router/index.js:321:12)
    at next (/app/node_modules/express/lib/router/index.js:261:10)
    at /app/cloud/app.js:150:9
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:82:5)
    at trim_prefix (/app/node_modules/express/lib/router/index.js:302:13)

These work fine on the live site hitting the hosted parse environment. Anyone else seen something like this? Is the csurf module interfering with the parse-server module? Any suggestions I should try? Thanks!

drew-gross commented 8 years ago

Parse Server doesn't use csrf, and the stack traces you posted are in the csurf module so I don't think this is caused by Parse Server. There may be some conflict in the request format used by csurf and Parse Server though.

woodardj commented 8 years ago

Roger that, thanks @drew-gross -- given that parse-server doesn't use csrf, does it smell like a middleware-ordering issue to you? Ie: the csurf stuff I'm using to protect the endpoints defined in my express app is interrupting the query (ultimately made via REST back the server, if I'm remembering correctly?). But it sounds like you're saying that the csurf module I'm including is being (incorrectly) invoked when requests are made against Parse Server's endpoints?

drew-gross commented 8 years ago

That sounds plausible but without being familiar with csurf I can't say for sure.