From 6e2589178b99f8f32f0ded9a7cfd9921c33e9b60 Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Sun, 24 Jan 2021 21:27:57 -0500 Subject: [PATCH] feat(auth): Add optional CSRF protection (#697) * fix(auth): Missing CSRF middleware Resolves LGTM alert/error for query js/missing-token-validation More info: https://lgtm.com/rules/1506064038914/ --- overseerr-api.yml | 3 ++ package.json | 2 ++ server/index.ts | 21 ++++++++++++- server/lib/settings.ts | 2 ++ src/components/Settings/SettingsMain.tsx | 31 ++++++++++++++++++ src/i18n/locale/en.json | 2 ++ yarn.lock | 40 ++++++++++++++++++++++-- 7 files changed, 98 insertions(+), 3 deletions(-) diff --git a/overseerr-api.yml b/overseerr-api.yml index 3e0510d01..d3c526067 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -62,6 +62,9 @@ components: applicationUrl: type: string example: https://os.example.com + csrfProtection: + type: boolean + example: false hideAvailable: type: boolean example: false diff --git a/package.json b/package.json index ad1be8a71..b696336c6 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "bowser": "^2.11.0", "connect-typeorm": "^1.1.4", "cookie-parser": "^1.4.5", + "csurf": "^1.11.0", "email-templates": "^8.0.3", "express": "^4.17.1", "express-openapi-validator": "^4.10.8", @@ -79,6 +80,7 @@ "@types/bcrypt": "^3.0.0", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", + "@types/csurf": "^1.11.0", "@types/email-templates": "^8.0.0", "@types/express": "^4.17.11", "@types/express-session": "^1.17.0", diff --git a/server/index.ts b/server/index.ts index 8ef6d6ed6..78621bab5 100644 --- a/server/index.ts +++ b/server/index.ts @@ -5,6 +5,7 @@ import { createConnection, getRepository } from 'typeorm'; import routes from './routes'; import bodyParser from 'body-parser'; import cookieParser from 'cookie-parser'; +import csurf from 'csurf'; import session, { Store } from 'express-session'; import { TypeormStore } from 'connect-typeorm/out'; import YAML from 'yamljs'; @@ -78,8 +79,26 @@ app next(); } }); + if (settings.main.csrfProtection) { + server.use( + csurf({ + cookie: { + httpOnly: true, + sameSite: true, + secure: !dev, + }, + }) + ); + server.use((req, res, next) => { + res.cookie('XSRF-TOKEN', req.csrfToken(), { + sameSite: true, + secure: !dev, + }); + next(); + }); + } - // Setup sessions + // Set up sessions const sessionRespository = getRepository(Session); server.use( '/api', diff --git a/server/lib/settings.ts b/server/lib/settings.ts index e1e947339..89bc3651a 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -48,6 +48,7 @@ export interface SonarrSettings extends DVRSettings { export interface MainSettings { apiKey: string; applicationUrl: string; + csrfProtection: boolean; defaultPermissions: number; hideAvailable: boolean; } @@ -155,6 +156,7 @@ class Settings { apiKey: '', applicationUrl: '', hideAvailable: false, + csrfProtection: false, defaultPermissions: Permission.REQUEST, }, plex: { diff --git a/src/components/Settings/SettingsMain.tsx b/src/components/Settings/SettingsMain.tsx index e29138f84..e886cd00c 100644 --- a/src/components/Settings/SettingsMain.tsx +++ b/src/components/Settings/SettingsMain.tsx @@ -27,6 +27,9 @@ const messages = defineMessages({ toastSettingsFailure: 'Something went wrong saving settings.', defaultPermissions: 'Default User Permissions', hideAvailable: 'Hide available media', + csrfProtection: 'Enable CSRF Protection', + csrfProtectionTip: + 'Sets external API access to read-only (Overseerr must be reloaded for changes to take effect)', }); const SettingsMain: React.FC = () => { @@ -72,6 +75,7 @@ const SettingsMain: React.FC = () => { { try { await axios.post('/api/v1/settings/main', { applicationUrl: values.applicationUrl, + csrfProtection: values.csrfProtection, defaultPermissions: values.defaultPermissions, hideAvailable: values.hideAvailable, }); @@ -165,6 +170,32 @@ const SettingsMain: React.FC = () => { +
+ +
+ { + setFieldValue('csrfProtection', !values.csrfProtection); + }} + className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox" + /> +
+