CSP Directives and hooks - sentry update

This commit is contained in:
matthieu42morin 2024-04-04 02:08:08 +02:00
parent b3c0a296bd
commit 687142b458
4 changed files with 135 additions and 22 deletions

83
src/cspDirectives.ts Normal file
View File

@ -0,0 +1,83 @@
// https://gist.github.com/acoyfellow/d8e86979c66ebea25e1643594e38be73, Rodney Lab
import {
PUBLIC_DOMAIN,
PUBLIC_SENTRY_KEY,
PUBLIC_SENTRY_PROJECT_ID,
PUBLIC_SENTRY_ORG_ID,
PUBLIC_WORKER_URL
} from '$env/static/public';
export const rootDomain = PUBLIC_DOMAIN; // or your server IP for dev
const directives = {
'base-uri': ["'self'"],
'child-src': ["'self'", 'blob:'],
// 'connect-src': ["'self'", 'ws://localhost:*'],
'connect-src': [
"'self'",
'ws://localhost:*',
'https://*.sentry.io',
'https://hcaptcha.com',
'https://*.hcaptcha.com',
'https://*.cartocdn.com',
PUBLIC_DOMAIN,
PUBLIC_WORKER_URL
],
'img-src': ["'self'", 'data:', 'https://images.unsplash.com'],
'font-src': ["'self'", 'data:'],
'form-action': ["'self'"],
'frame-ancestors': ["'self'"],
'frame-src': [
"'self'",
// "https://*.stripe.com",
// "https://*.facebook.com",
// "https://*.facebook.net",
'https://hcaptcha.com',
'https://*.hcaptcha.com',
'https://www.openstreetmap.org',
'https://*.cartocdn.com'
],
'manifest-src': ["'self'"],
'media-src': ["'self'", 'data:'],
'object-src': ["'none'"],
// 'style-src': ["'self'", "'unsafe-inline'"],
'style-src': ["'self'", "'unsafe-inline'", 'https://hcaptcha.com', 'https://*.hcaptcha.com'],
'default-src': [
"'self'",
rootDomain,
`ws://${rootDomain}`,
// 'https://*.google.com',
// 'https://*.googleapis.com',
// 'https://*.firebase.com',
// 'https://*.gstatic.com',
// 'https://*.cloudfunctions.net',
// 'https://*.algolia.net',
// 'https://*.facebook.com',
// 'https://*.facebook.net',
// 'https://*.stripe.com',
'https://*.sentry.io'
],
'script-src': [
"'self'",
"'unsafe-inline'",
// 'https://*.stripe.com',
// 'https://*.facebook.com',
// 'https://*.facebook.net',
'https://hcaptcha.com',
'https://*.hcaptcha.com',
'https://*.sentry.io',
// 'https://polyfill.io',
'https://*.cartocdn.com'
],
'worker-src': ["'self'", 'blob:'],
//report-to can throw "Content-Security-Policy: Couldnt process unknown directive report-to", leave it for older browsers.
'report-to': ["'csp-endpoint'"],
'report-uri': [
`https://${PUBLIC_SENTRY_ORG_ID}.ingest.us.sentry.io/api/${PUBLIC_SENTRY_PROJECT_ID}/security/?sentry_key=${PUBLIC_SENTRY_KEY}`
]
};
export const csp = Object.entries(directives)
.map(([key, arr]) => key + ' ' + arr.join(' '))
.join('; ');

View File

@ -1,14 +1,14 @@
import { handleErrorWithSentry, replayIntegration } from '@sentry/sveltekit'; import { handleErrorWithSentry, replayIntegration } from '@sentry/sveltekit';
import * as Sentry from '@sentry/sveltekit'; import * as Sentry from '@sentry/sveltekit';
import { SENTRY_DSN } from '$env/dynamic/private'; import {
import { NODE_ENV } from '$env/static/private'; PUBLIC_SENTRY_KEY,
PUBLIC_SENTRY_PROJECT_ID,
PUBLIC_SENTRY_ORG_ID
} from '$env/static/public';
Sentry.init({ Sentry.init({
dsn: SENTRY_DSN, dsn: `https://${PUBLIC_SENTRY_KEY}@${PUBLIC_SENTRY_ORG_ID}.ingest.us.sentry.io/${PUBLIC_SENTRY_PROJECT_ID}`,
tracesSampleRate: 1.0,
if (NODE_ENV === 'production') {
tracesSampleRate: 1.0,
} else {},
// This sets the sample rate to be 10%. You may want this to be 100% while // This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production // in development and sample at a lower rate in production

View File

@ -1,12 +1,52 @@
import type { Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks'; import { sequence } from '@sveltejs/kit/hooks';
import { handleErrorWithSentry, sentryHandle } from '@sentry/sveltekit';
import * as Sentry from '@sentry/sveltekit'; import * as Sentry from '@sentry/sveltekit';
import { SENTRY_DSN } from '$env/dynamic/private'; import {
PUBLIC_SENTRY_KEY,
PUBLIC_SENTRY_PROJECT_ID,
PUBLIC_SENTRY_ORG_ID
} from '$env/static/public';
import { csp, rootDomain } from './cspDirectives';
Sentry.init({ Sentry.init({
dsn: SENTRY_DSN, dsn: `https://${PUBLIC_SENTRY_KEY}@${PUBLIC_SENTRY_ORG_ID}.ingest.us.sentry.io/${PUBLIC_SENTRY_PROJECT_ID}`,
tracesSampleRate: 1 tracesSampleRate: 1.0
}); });
export const handleError = Sentry.handleErrorWithSentry(); export const cspHandle: Handle = async ({ event, resolve }) => {
if (!csp) {
throw new Error('csp is undefined');
}
const response = await resolve(event);
export const handle = sequence(Sentry.sentryHandle()); // Permission fullscreen necessary for maps fullscreen
const headers = {
'X-Frame-Options': 'SAMEORIGIN',
'Referrer-Policy': 'no-referrer',
'Permissions-Policy': `accelerometer=(), autoplay=(), camera=(), document-domain=(self, 'js-profiling'), encrypted-media=(), fullscreen=(self ${rootDomain}), gyroscope=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), sync-xhr=(), usb=(), xr-spatial-tracking=(), geolocation=()`,
'X-Content-Type-Options': 'nosniff',
// 'Content-Security-Policy-Report-Only': csp,
'Content-Security-Policy': csp,
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
'Expect-CT': `max-age=86400, report-uri="https://${PUBLIC_SENTRY_ORG_ID}.ingest.us.sentry.io/api/${PUBLIC_SENTRY_PROJECT_ID}/security/?sentry_key=${PUBLIC_SENTRY_KEY}"`,
'Report-To': `{group: "csp-endpoint", "max_age": 10886400, "endpoints": [{"url": "https://${PUBLIC_SENTRY_ORG_ID}.ingest.us.sentry.io/api/${PUBLIC_SENTRY_PROJECT_ID}/security/?sentry_key=${PUBLIC_SENTRY_KEY}"}]}`
};
Object.entries(headers).forEach(([key, value]) => {
response.headers.set(key, value);
});
return response;
};
// If you have custom handlers, make sure to place them after `sentryHandle()` in the `sequence` function.
export const handle: Handle = sequence(sentryHandle(), cspHandle);
// If you have a custom error handler, pass it to `handleErrorWithSentry`
export const handleError = handleErrorWithSentry();
// https://gist.github.com/acoyfellow/d8e86979c66ebea25e1643594e38be73
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
// https://scotthelme.co.uk/content-security-policy-an-introduction/
// scanner: https://securityheaders.com/

View File

@ -1,10 +0,0 @@
import type { Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
const handleCSP: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
// Avoid clickjacking attacks, see https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html
response.headers.set('Content-Security-Policy', 'frame-ancestors *.mattmor.in *;');
return response;
};
export const handle = sequence(handleCSP);