Self-host front-end libraries, minimal sign-in header and CSP
- sign-in pages load only what they use (include_login_header.php, ajax_core.js) - every library, font and data file served from assets/vendor with pinned versions (VERSIONS.json); no CDN at runtime - Content-Security-Policy on app, sign-in and landing pages - remove httpbin Dropzone target and source-map references
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* page_headers.php — security headers for HTML pages (app pages, sign-in pages).
|
||||
*
|
||||
* Call send_page_security_headers() before any output. The Content-Security-Policy
|
||||
* lists what the pages actually load:
|
||||
* - scripts, styles, fonts and data files are all self-hosted under assets/vendor/
|
||||
* (versions in assets/vendor/VERSIONS.json), so no CDN host is allowed;
|
||||
* - the Node.js real-time server (NODE_PUBLIC_URL) serves socket.io.js and the
|
||||
* WebSocket connection;
|
||||
* - 'unsafe-inline' because pages use inline <script> blocks and onclick=
|
||||
* handlers; 'unsafe-eval' because alasql compiles its queries with new Function.
|
||||
*/
|
||||
|
||||
if (!function_exists('send_page_security_headers')) {
|
||||
function send_page_security_headers(): void {
|
||||
if (headers_sent()) return;
|
||||
|
||||
$script = ["'self'", "'unsafe-inline'", "'unsafe-eval'"];
|
||||
$connect = ["'self'"];
|
||||
|
||||
if (defined('NODE_PUBLIC_URL')) {
|
||||
$node = parse_url(NODE_PUBLIC_URL);
|
||||
if (!empty($node['scheme']) && !empty($node['host'])) {
|
||||
$origin = $node['host'] . (isset($node['port']) ? ':' . $node['port'] : '');
|
||||
$secure = strtolower($node['scheme']) === 'https';
|
||||
$script[] = ($secure ? 'https://' : 'http://') . $origin;
|
||||
$connect[] = ($secure ? 'https://' : 'http://') . $origin;
|
||||
$connect[] = ($secure ? 'wss://' : 'ws://') . $origin;
|
||||
}
|
||||
}
|
||||
|
||||
$csp = implode('; ', [
|
||||
"default-src 'self'",
|
||||
'script-src ' . implode(' ', $script),
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"font-src 'self' data:",
|
||||
"img-src 'self' data: blob:",
|
||||
"media-src 'self' blob:",
|
||||
'connect-src ' . implode(' ', $connect),
|
||||
"worker-src 'self' blob:",
|
||||
"frame-src 'self' blob:",
|
||||
"object-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"frame-ancestors 'self'",
|
||||
]);
|
||||
|
||||
header('Content-Security-Policy: ' . $csp, true);
|
||||
header('X-Content-Type-Options: nosniff', true);
|
||||
header('X-Frame-Options: SAMEORIGIN', true);
|
||||
header('Referrer-Policy: strict-origin-when-cross-origin', true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user