From 36e978907000210338e29697cd283e3231c50dcf Mon Sep 17 00:00:00 2001 From: rubikscraft Date: Thu, 31 Mar 2022 22:40:11 +0200 Subject: [PATCH] add security headers and CORS --- backend/package.json | 1 + backend/src/main.ts | 13 +++--- .../src/routes/image/imageroute.controller.ts | 1 + backend/src/routes/image/imageroute.module.ts | 14 +++++- backend/src/security.ts | 45 +++++++++++++++++++ yarn.lock | 13 ++++++ 6 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 backend/src/security.ts diff --git a/backend/package.json b/backend/package.json index b664e18..f0fa361 100644 --- a/backend/package.json +++ b/backend/package.json @@ -31,6 +31,7 @@ "bcrypt": "^5.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", + "fastify-helmet": "^7.0.1", "fastify-multipart": "^5.3.1", "fastify-static": "^4.6.1", "file-type": "^17.1.1", diff --git a/backend/src/main.ts b/backend/src/main.ts index 45006a4..1ae84a8 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -4,6 +4,7 @@ import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; +import fastifyHelmet from 'fastify-helmet'; import * as multipart from 'fastify-multipart'; import { ValidateOptions } from 'picsur-shared/dist/util/validate'; import { AppModule } from './app.module'; @@ -13,12 +14,14 @@ import { MainExceptionFilter } from './layers/httpexception/httpexception.filter import { SuccessInterceptor } from './layers/success/success.interceptor'; import { PicsurLoggerService } from './logger/logger.service'; import { MainAuthGuard } from './managers/auth/guards/main.guard'; +import { HelmetOptions } from './security'; async function bootstrap() { // Create fasify const fastifyAdapter = new FastifyAdapter(); // TODO: generic error messages - fastifyAdapter.register(multipart as any); + await fastifyAdapter.register(multipart as any); + await fastifyAdapter.register(fastifyHelmet, HelmetOptions); // Create nest app const app = await NestFactory.create( @@ -28,16 +31,16 @@ async function bootstrap() { bufferLogs: true, }, ); + // app.enableCors({ + // origin: 'self' + // }); // Configure nest app app.useGlobalFilters(new MainExceptionFilter()); app.useGlobalInterceptors(new SuccessInterceptor()); app.useGlobalPipes(new ValidationPipe(ValidateOptions)); app.useGlobalGuards( - new MainAuthGuard( - app.get(Reflector), - app.get(UsersService), - ), + new MainAuthGuard(app.get(Reflector), app.get(UsersService)), ); // Configure logger diff --git a/backend/src/routes/image/imageroute.controller.ts b/backend/src/routes/image/imageroute.controller.ts index 15360a8..61a5eb0 100644 --- a/backend/src/routes/image/imageroute.controller.ts +++ b/backend/src/routes/image/imageroute.controller.ts @@ -17,6 +17,7 @@ import { Permission } from '../../models/dto/permissions.dto'; import { ImageUploadDto } from '../../models/requests/imageroute.dto'; import { ImageIdValidator } from './imageid.validator'; +// This is the only controller with CORS enabled @Controller('i') @RequiredPermissions(Permission.ImageView) export class ImageController { diff --git a/backend/src/routes/image/imageroute.module.ts b/backend/src/routes/image/imageroute.module.ts index 0f048db..64b257d 100644 --- a/backend/src/routes/image/imageroute.module.ts +++ b/backend/src/routes/image/imageroute.module.ts @@ -1,12 +1,22 @@ -import { Module } from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; +import cors from 'cors'; import { DecoratorsModule } from '../../decorators/decorators.module'; import { ImageManagerModule } from '../../managers/imagemanager/imagemanager.module'; import { ImageIdValidator } from './imageid.validator'; import { ImageController } from './imageroute.controller'; +const corsConfig = cors({ + // 48 hours + maxAge: 1728000, +}); + @Module({ imports: [ImageManagerModule, DecoratorsModule], providers: [ImageIdValidator], controllers: [ImageController], }) -export class ImageModule {} +export class ImageModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(corsConfig).forRoutes('/i'); + } +} diff --git a/backend/src/security.ts b/backend/src/security.ts new file mode 100644 index 0000000..e6cc240 --- /dev/null +++ b/backend/src/security.ts @@ -0,0 +1,45 @@ +import { FastifyHelmetOptions } from 'fastify-helmet'; + +export const HelmetOptions: FastifyHelmetOptions = { + contentSecurityPolicy: { + directives: { + 'default-src': ["'self'"], + 'base-uri': ["'self'"], + 'block-all-mixed-content': [], + 'form-action': ["'self'"], + 'frame-ancestors': ["'self'"], + 'img-src': ["'self'", 'data:', 'blob:'], + 'object-src': ["'none'"], + 'script-src': ["'self'"], + 'style-src': ["'self'", "'unsafe-inline'"], + 'upgrade-insecure-requests': [], + }, + useDefaults: false, + reportOnly: false, + }, + // Require any external content to have CORS set + crossOriginEmbedderPolicy: true, + // Destroy reference to global object on new page + crossOriginOpenerPolicy: true, + crossOriginResourcePolicy: true, + // Dont fully understand the purpose of this + // But pretty sure we dont need it + expectCt: false, + // Do not send referrer header + referrerPolicy: true, + // Ensure browser doesnt connect with HTTP + hsts: true, + noSniff: true, + originAgentCluster: true, + // Prevent prefetching of dns + dnsPrefetchControl: true, + // We aint targeting IE, but it cant hurt + ieNoOpen: true, + // Only allow in iframe of same origin + frameguard: true, + permittedCrossDomainPolicies: true, + hidePoweredBy: true, + // Requires nonce for every stylesheet, this is too much work so we disable it + enableCSPNonces: false, + xssFilter: true, +}; diff --git a/yarn.lock b/yarn.lock index f933e5f..167416c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4029,6 +4029,14 @@ fastify-formbody@5.2.0: dependencies: fastify-plugin "^3.0.0" +fastify-helmet@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fastify-helmet/-/fastify-helmet-7.0.1.tgz#506c288dc5a5bea4d7332c1b5aed8f69a3fe383b" + integrity sha512-/PSKDJZkBhJ0ioY1swOUfYNYQRfRnXD5X2eKD9TSVBuPKbH+OYg/IYBxad33uaOGlWn28MNOd+kw5sQKSWY1yA== + dependencies: + fastify-plugin "^3.0.0" + helmet "^5.0.1" + fastify-multipart@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/fastify-multipart/-/fastify-multipart-5.3.1.tgz#05254d8aa43dc02af6ce01f4e513a6c30bea2886" @@ -4462,6 +4470,11 @@ hdr-histogram-percentiles-obj@^3.0.0: resolved "https://registry.yarnpkg.com/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz#9409f4de0c2dda78e61de2d9d78b1e9f3cba283c" integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw== +helmet@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-5.0.2.tgz#3264ec6bab96c82deaf65e3403c369424cb2366c" + integrity sha512-QWlwUZZ8BtlvwYVTSDTBChGf8EOcQ2LkGMnQJxSzD1mUu8CCjXJZq/BXP8eWw4kikRnzlhtYo3lCk0ucmYA3Vg== + hexoid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"