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/
pull/726/head
TheCatLady 4 years ago committed by GitHub
parent 4b0241c3b3
commit 6e2589178b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -62,6 +62,9 @@ components:
applicationUrl:
type: string
example: https://os.example.com
csrfProtection:
type: boolean
example: false
hideAvailable:
type: boolean
example: false

@ -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",

@ -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',

@ -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: {

@ -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 = () => {
<Formik
initialValues={{
applicationUrl: data?.applicationUrl,
csrfProtection: data?.csrfProtection,
defaultPermissions: data?.defaultPermissions ?? 0,
hideAvailable: data?.hideAvailable,
}}
@ -80,6 +84,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 = () => {
</div>
</div>
</div>
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
<label
htmlFor="csrfProtection"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
>
<div className="flex flex-col">
<span className="mr-2">
{intl.formatMessage(messages.csrfProtection)}
</span>
<span className="text-gray-500">
{intl.formatMessage(messages.csrfProtectionTip)}
</span>
</div>
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<Field
type="checkbox"
id="csrfProtection"
name="csrfProtection"
onChange={() => {
setFieldValue('csrfProtection', !values.csrfProtection);
}}
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
/>
</div>
</div>
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
<label
htmlFor="name"

@ -378,6 +378,8 @@
"components.Settings.autoapprovedrequests": "Send notifications for auto-approved requests",
"components.Settings.cancelscan": "Cancel Scan",
"components.Settings.copied": "Copied API key to clipboard",
"components.Settings.csrfProtection": "Enable CSRF Protection",
"components.Settings.csrfProtectionTip": "Sets external API access to read-only (Overseerr must be reloaded for changes to take effect)",
"components.Settings.currentlibrary": "Current Library: {name}",
"components.Settings.default": "Default",
"components.Settings.default4k": "Default 4K",

@ -2013,6 +2013,13 @@
dependencies:
"@types/express" "*"
"@types/csurf@^1.11.0":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@types/csurf/-/csurf-1.11.0.tgz#2809e89f55f12a2df8cd2826c06dfd66600dd14d"
integrity sha512-IwqGRWImcbIdwumGprYR0EgIZ7GAklOIGaNqe2u7jb0YUilg7yrrxXth11VA/AJK8wQWYHxTQagkCE75oaoBvQ==
dependencies:
"@types/express-serve-static-core" "*"
"@types/debug@0.0.31":
version "0.0.31"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-0.0.31.tgz#bac8d8aab6a823e91deb7f79083b2a35fa638f33"
@ -4647,6 +4654,15 @@ crypto-random-string@^2.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
csrf@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/csrf/-/csrf-3.1.0.tgz#ec75e9656d004d674b8ef5ba47b41fbfd6cb9c30"
integrity sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==
dependencies:
rndm "1.2.0"
tsscmp "1.0.6"
uid-safe "2.1.5"
css-blank-pseudo@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5"
@ -4822,6 +4838,16 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.2.tgz#ee5ff8f208c8cd613b389f7b222c9801ca62b3f7"
integrity sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==
csurf@^1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/csurf/-/csurf-1.11.0.tgz#ab0c3c6634634192bd3d6f4b861be20800eeb61a"
integrity sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==
dependencies:
cookie "0.4.0"
cookie-signature "1.0.6"
csrf "3.1.0"
http-errors "~1.7.3"
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@ -7053,7 +7079,7 @@ http-errors@1.7.2:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@1.7.3, http-errors@~1.7.2:
http-errors@1.7.3, http-errors@~1.7.2, http-errors@~1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
@ -12188,6 +12214,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rndm@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/rndm/-/rndm-1.2.0.tgz#f33fe9cfb52bbfd520aa18323bc65db110a1b76c"
integrity sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=
run-async@^2.2.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@ -13628,6 +13659,11 @@ tslib@^2.0.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.2.tgz#462295631185db44b21b1ea3615b63cd1c038242"
integrity sha512-wAH28hcEKwna96/UacuWaVspVLkg4x1aDM9JlzqaQTOFczCktkVAb5fmXChgandR1EraDPs2w8P+ozM+oafwxg==
tsscmp@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"
integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
@ -13771,7 +13807,7 @@ uid-number@0.0.6:
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=
uid-safe@~2.1.5:
uid-safe@2.1.5, uid-safe@~2.1.5:
version "2.1.5"
resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a"
integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==

Loading…
Cancel
Save