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:
@@ -1,5 +1,6 @@
|
||||
import { Computed, Disposable, dom, domComputed, DomContents, input, MultiHolder, Observable, styled } from "grainjs";
|
||||
|
||||
import { submitForm } from "app/client/lib/uploads";
|
||||
import { AppModel, reportError } from "app/client/models/AppModel";
|
||||
import { urlState } from "app/client/models/gristUrlState";
|
||||
import { AccountWidget } from "app/client/ui/AccountWidget";
|
||||
@@ -59,11 +60,16 @@ export class WelcomePage extends Disposable {
|
||||
return form = dom(
|
||||
'form',
|
||||
{ method: "post" },
|
||||
dom.on('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this._submitForm(form).catch(reportError);
|
||||
return false;
|
||||
}),
|
||||
cssLabel('Your full name, as you\'d like it displayed to your collaborators.'),
|
||||
inputEl = cssInput(
|
||||
value, { onInput: true, },
|
||||
{ name: "username" },
|
||||
dom.onKeyDown({Enter: () => isNameValid.get() && form.submit()}),
|
||||
dom.onKeyDown({Enter: () => isNameValid.get() && this._submitForm(form).catch(reportError)}),
|
||||
),
|
||||
dom.maybe((use) => use(value) && !use(isNameValid), buildNameWarningsDom),
|
||||
cssButtonGroup(
|
||||
@@ -76,6 +82,15 @@ export class WelcomePage extends Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
private async _submitForm(form: HTMLFormElement) {
|
||||
const result = await submitForm(form);
|
||||
const redirectUrl = result.redirectUrl;
|
||||
if (!redirectUrl) {
|
||||
throw new Error('form failed to redirect');
|
||||
}
|
||||
window.location.assign(redirectUrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
private async _fetchOrgs() {
|
||||
this._orgs = await this._appModel.api.getOrgs(true);
|
||||
|
||||
Reference in New Issue
Block a user