Replace unsafe-inline with CSP nonces for script-src
SvelteKit now handles script-src with automatic nonces via its built-in CSP support. The hooks handler merges extra directives (style-src, font-src, img-src, etc.) onto SvelteKit's nonce-based policy for page responses, and sets a full CSP for API responses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-1
@@ -100,7 +100,17 @@ export const handle: Handle = async ({ event, resolve }) => {
|
|||||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||||
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||||||
response.headers.set('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ws: wss:; frame-ancestors 'none'");
|
// SvelteKit sets CSP with nonces on page responses via svelte.config.js.
|
||||||
|
// For non-page responses (API, etc.) or to add directives SvelteKit doesn't cover, set our own.
|
||||||
|
const existingCSP = response.headers.get('content-security-policy');
|
||||||
|
if (existingCSP) {
|
||||||
|
// SvelteKit set a CSP with script-src nonce — append our extra directives
|
||||||
|
const extras = "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ws: wss:; frame-ancestors 'none'";
|
||||||
|
response.headers.set('Content-Security-Policy', `${existingCSP}; ${extras}`);
|
||||||
|
} else {
|
||||||
|
// Non-page response (API routes) — set full CSP without unsafe-inline
|
||||||
|
response.headers.set('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ws: wss:; frame-ancestors 'none'");
|
||||||
|
}
|
||||||
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()');
|
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()');
|
||||||
|
|
||||||
logger.info({
|
logger.info({
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ const config = {
|
|||||||
$components: 'src/lib/components',
|
$components: 'src/lib/components',
|
||||||
$server: 'src/lib/server',
|
$server: 'src/lib/server',
|
||||||
$utils: 'src/lib/utils'
|
$utils: 'src/lib/utils'
|
||||||
|
},
|
||||||
|
csp: {
|
||||||
|
directives: {
|
||||||
|
'default-src': ['self'],
|
||||||
|
'script-src': ['self']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user