Fix production site issues with a more permissive CSP policy
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
767df7dc44
commit
7845079307
|
@ -54,19 +54,20 @@ if CSP_CSS_HASH:
|
||||||
if CSP_CUSTOM_CSS_HASH:
|
if CSP_CUSTOM_CSS_HASH:
|
||||||
logger.info(f"Using pre-calculated CSP hash for custom CSS: sha256-{CSP_CUSTOM_CSS_HASH}")
|
logger.info(f"Using pre-calculated CSP hash for custom CSS: sha256-{CSP_CUSTOM_CSS_HASH}")
|
||||||
|
|
||||||
# Define the base CSP for UI routes
|
# Define a more permissive CSP that works for both UI and API routes
|
||||||
ui_csp = {
|
# This is less secure but ensures compatibility with all clients
|
||||||
'default-src': "'none'", # Deny by default
|
csp = {
|
||||||
'script-src': ["'self'"] + ([f"'sha256-{CSP_JS_HASH}'"] if CSP_JS_HASH else []),
|
'default-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'", "data:", "blob:"],
|
||||||
'style-src': ["'self'"] +
|
'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'"] + ([f"'sha256-{CSP_JS_HASH}'"] if CSP_JS_HASH else []),
|
||||||
|
'style-src': ["'self'", "'unsafe-inline'"] +
|
||||||
([f"'sha256-{CSP_CSS_HASH}'"] if CSP_CSS_HASH else []) +
|
([f"'sha256-{CSP_CSS_HASH}'"] if CSP_CSS_HASH else []) +
|
||||||
([f"'sha256-{CSP_CUSTOM_CSS_HASH}'"] if CSP_CUSTOM_CSS_HASH else []),
|
([f"'sha256-{CSP_CUSTOM_CSS_HASH}'"] if CSP_CUSTOM_CSS_HASH else []),
|
||||||
'img-src': ["'self'", "data:"],
|
'img-src': ["'self'", "data:", "blob:"],
|
||||||
'font-src': ["'self'"],
|
'font-src': ["'self'", "data:"],
|
||||||
'connect-src': "'self'",
|
'connect-src': ["'self'", "*"],
|
||||||
'manifest-src': "'self'",
|
'manifest-src': "'self'",
|
||||||
'object-src': "'none'", # Explicitly disallow objects
|
'object-src': "'none'", # Still explicitly disallow objects
|
||||||
'frame-ancestors': "'none'", # Prevent framing
|
'frame-ancestors': "'none'", # Still prevent framing
|
||||||
'base-uri': "'self'",
|
'base-uri': "'self'",
|
||||||
'form-action': "'self'"
|
'form-action': "'self'"
|
||||||
}
|
}
|
||||||
|
@ -75,18 +76,16 @@ ui_csp = {
|
||||||
if APP_DOMAIN:
|
if APP_DOMAIN:
|
||||||
logger.info(f"Configuring CSP for domain: {APP_DOMAIN}")
|
logger.info(f"Configuring CSP for domain: {APP_DOMAIN}")
|
||||||
# Add domain to connect-src if needed
|
# Add domain to connect-src if needed
|
||||||
if APP_DOMAIN not in ui_csp['connect-src']:
|
if APP_DOMAIN not in csp['connect-src']:
|
||||||
if isinstance(ui_csp['connect-src'], list):
|
if isinstance(csp['connect-src'], list):
|
||||||
ui_csp['connect-src'].append(APP_DOMAIN)
|
csp['connect-src'].append(APP_DOMAIN)
|
||||||
else:
|
|
||||||
ui_csp['connect-src'] = [ui_csp['connect-src'], APP_DOMAIN]
|
|
||||||
|
|
||||||
# Update form-action to include the domain
|
# Update form-action to include the domain
|
||||||
if isinstance(ui_csp['form-action'], list):
|
if isinstance(csp['form-action'], list):
|
||||||
if APP_DOMAIN not in ui_csp['form-action']:
|
if APP_DOMAIN not in csp['form-action']:
|
||||||
ui_csp['form-action'].append(APP_DOMAIN)
|
csp['form-action'].append(APP_DOMAIN)
|
||||||
else:
|
else:
|
||||||
ui_csp['form-action'] = [ui_csp['form-action'], APP_DOMAIN]
|
csp['form-action'] = [csp['form-action'], APP_DOMAIN]
|
||||||
|
|
||||||
# Configure Permissions-Policy (formerly Feature-Policy)
|
# Configure Permissions-Policy (formerly Feature-Policy)
|
||||||
# Deny access to all browser features that we don't need
|
# Deny access to all browser features that we don't need
|
||||||
|
@ -132,11 +131,10 @@ additional_headers = {
|
||||||
'Cross-Origin-Opener-Policy': 'same-origin'
|
'Cross-Origin-Opener-Policy': 'same-origin'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize Talisman with the static CSP configuration
|
# Initialize Talisman with the permissive CSP configuration
|
||||||
# We'll handle API routes separately in the after_request handler
|
|
||||||
talisman = Talisman(
|
talisman = Talisman(
|
||||||
app,
|
app,
|
||||||
content_security_policy=ui_csp,
|
content_security_policy=csp,
|
||||||
content_security_policy_nonce_in=['script-src'],
|
content_security_policy_nonce_in=['script-src'],
|
||||||
feature_policy=permissions_policy,
|
feature_policy=permissions_policy,
|
||||||
force_https=force_https,
|
force_https=force_https,
|
||||||
|
@ -151,7 +149,7 @@ talisman = Talisman(
|
||||||
session_cookie_http_only=True
|
session_cookie_http_only=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add CORS headers for API routes and handle CSP
|
# Add CORS headers for API routes
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def add_api_headers(response):
|
def add_api_headers(response):
|
||||||
if request.path.startswith('/api/'):
|
if request.path.startswith('/api/'):
|
||||||
|
@ -160,10 +158,6 @@ def add_api_headers(response):
|
||||||
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
|
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
|
||||||
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
|
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
|
||||||
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
|
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
|
||||||
|
|
||||||
# Remove CSP for API routes to ensure compatibility with clients
|
|
||||||
if 'Content-Security-Policy' in response.headers:
|
|
||||||
del response.headers['Content-Security-Policy']
|
|
||||||
else:
|
else:
|
||||||
# For UI routes, add additional security headers
|
# For UI routes, add additional security headers
|
||||||
for header, value in additional_headers.items():
|
for header, value in additional_headers.items():
|
||||||
|
|
Loading…
Reference in New Issue