diff --git a/backend/package.json b/backend/package.json index cb7034fc..41841e45 100644 --- a/backend/package.json +++ b/backend/package.json @@ -30,7 +30,8 @@ "rxjs": "^7.8.1", "sharp": "^0.33.5", "tesseract.js": "^6.0.1", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "helmet": "^8.0.0" }, "devDependencies": { "@nestjs/cli": "^10.3.0", diff --git a/backend/src/main.ts b/backend/src/main.ts index 372d4024..0a28abf1 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -2,10 +2,31 @@ import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { GlobalExceptionFilter } from './common/filters/global-exception.filter'; +import helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); + // Helmet som säkerhetsbackup (CSP hanteras av Next.js/Caddy) + app.use( + helmet({ + contentSecurityPolicy: false, + crossOriginEmbedderPolicy: true, + crossOriginOpenerPolicy: { policy: 'same-origin' }, + crossOriginResourcePolicy: { policy: 'same-origin' }, + originAgentCluster: true, + referrerPolicy: { policy: 'strict-origin-when-cross-origin' }, + strictTransportSecurity: { + maxAge: 31536000, + includeSubDomains: true, + preload: true, + }, + xContentTypeOptions: true, + xFrameOptions: { action: 'deny' }, + xXssProtection: false, // Deprecated, hanteras av Caddy + }), + ); + app.setGlobalPrefix('api'); // Registrera global exception filter diff --git a/frontend/next.config.js b/frontend/next.config.js index 76da2edf..7e040ff0 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -9,6 +9,33 @@ const nextConfig = { }, ]; }, + async headers() { + const csp = [ + "default-src 'self'", + "script-src 'self' 'unsafe-eval' 'unsafe-inline'", + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com", + "img-src 'self' data: https:", + "font-src 'self' https://fonts.gstatic.com", + "connect-src 'self' https://api.mistral.ai", + "frame-src 'none'", + "object-src 'none'", + "base-uri 'self'", + "form-action 'self'", + "upgrade-insecure-requests", + ].join('; '); + + return [ + { + source: '/:path*', + headers: [ + { + key: 'Content-Security-Policy', + value: csp, + }, + ], + }, + ]; + }, }; module.exports = nextConfig;