mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) mitigate csrf by requiring custom header for unsafe methods
Summary: For methods other than `GET`, `HEAD`, and `OPTIONS`, allow cookie-based authentication only if a certain custom header is present. Specifically, we check that `X-Requested-With` is set to `XMLHttpRequest`. This is somewhat arbitrary, but allows us to use https://expressjs.com/en/api.html#req.xhr. A request send from a browser that sets a custom header will prompt a preflight check, giving us a chance to check if the origin is trusted. This diff deals with getting the header in place. There will be more work to do after this: * Make sure that all important endpoints are checking origin. Skimming code, /api endpoint check origin, and some but not all others. * Add tests spot-testing origin checks. * Check on cases that authenticate differently. - Check the websocket endpoint - it can be connected to from an arbitrary site; there is per-doc access control but probably better to lock it down more. - There may be old endpoints that authenticate based on knowledge of a client id rather than cookies. Test Plan: added a test Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2631
This commit is contained in:
@@ -8,7 +8,7 @@ import {RequestWithGrist} from 'app/server/lib/FlexServer';
|
||||
import {GristServer} from 'app/server/lib/GristServer';
|
||||
import {guessExt} from 'app/server/lib/guessExt';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {optStringParam, trustOrigin} from 'app/server/lib/requestUtils';
|
||||
import {optStringParam} from 'app/server/lib/requestUtils';
|
||||
import {isPathWithin} from 'app/server/lib/serverUtils';
|
||||
import * as shutdown from 'app/server/lib/shutdown';
|
||||
import {fromCallback} from 'bluebird';
|
||||
@@ -42,18 +42,13 @@ export function addUploadRoute(server: GristServer, expressApp: Application, ...
|
||||
|
||||
// When doing a cross-origin post, the browser will check for access with options prior to posting.
|
||||
// We need to reassure it that the request will be accepted before it will go ahead and post.
|
||||
expressApp.options([`/${UPLOAD_URL_PATH}`, '/copy'], async (req: Request, res: Response) => {
|
||||
if (!trustOrigin(req, res)) { return res.status(500).send(); }
|
||||
res.header("Access-Control-Allow-Credentials", "true");
|
||||
expressApp.options([`/${UPLOAD_URL_PATH}`, '/copy'], ...handlers, async (req, res) => {
|
||||
// Origin is checked by middleware - if we get this far, we are ok.
|
||||
res.status(200).send();
|
||||
});
|
||||
|
||||
expressApp.post(`/${UPLOAD_URL_PATH}`, ...handlers, expressWrap(async (req: Request, res: Response) => {
|
||||
try {
|
||||
if (!trustOrigin(req, res)) {
|
||||
throw new Error('Unrecognized origin');
|
||||
}
|
||||
res.header("Access-Control-Allow-Credentials", "true");
|
||||
const uploadResult: UploadResult = await handleUpload(req, res);
|
||||
res.status(200).send(JSON.stringify(uploadResult));
|
||||
} catch (err) {
|
||||
@@ -67,10 +62,6 @@ export function addUploadRoute(server: GristServer, expressApp: Application, ...
|
||||
|
||||
// Like upload, but copy data from a document already known to us.
|
||||
expressApp.post(`/copy`, ...handlers, expressWrap(async (req: Request, res: Response) => {
|
||||
if (!trustOrigin(req, res)) {
|
||||
throw new Error('Unrecognized origin');
|
||||
}
|
||||
res.header("Access-Control-Allow-Credentials", "true");
|
||||
const docId = optStringParam(req.query.doc);
|
||||
const name = optStringParam(req.query.name);
|
||||
if (!docId) { throw new Error('doc must be specified'); }
|
||||
|
||||
Reference in New Issue
Block a user