User Story
As a webmaster, I want to use CSP to secure my application against attacks.
Currently, ZK's client-engine requires the following CSP headers: unsafe-inline, unsafe-eval
These CSP rules are necessary for the Client engine to execute scripts dynamically by creating new script elements in the page, and by creating new Functions for JSON eval and Widget $define instantiation
The goal is to allow the developer to set stricter CSP, which do not allow all sources unsafe-eval or unsafe-inline.
Acceptance Criteria
1. CSP Configuration & Policy
AC-1.1: Default Behavior (Backward Compatibility)
- By default, CSP is disabled
- Without configuration, ZK outputs the same headers and scripts as current implementation
- Existing applications must work without any code changes when upgrading
- CSP feature is opt-in only
AC-1.2: Enable Basic CSP with Default Policy (HIGH PRIORITY)
- Provide simple boolean flag to enable CSP
<system-config> <csp-enabled>true</csp-enabled> </system-config>
- Default policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'
- This allows ZK's client-engine to function without code changes
- Provides basic CSP protection while maintaining full backward compatibility
- Reference: https://docs.zkoss.org/zk_dev_ref/security_tips/content_security_policy
AC-1.3: Enable Strict-Dynamic Mode (HIGH PRIORITY)
- Additional property to enable nonce-based strict CSP
<system-config> <csp-enabled>true</csp-enabled> <csp-strict-dynamic-enabled>true</csp-strict-dynamic-enabled> </system-config>
- Strict-dynamic policy: script-src 'self' 'strict-dynamic' 'nonce-{generated}'
- Removes 'unsafe-inline' and 'unsafe-eval' from policy
- Automatically generates and applies nonces to all ZK scripts
- More secure but may require application code review
- Disabled by default (CSP3 feature, opt-in)
AC-1.4: Custom Policy Override (HIGH PRIORITY)
- Allow developers to override default policy via XML configuration
- Example for default mode:
<system-config> <csp-enabled>true</csp-enabled> <csp-policy>script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com</csp-policy> </system-config>
- Example for strict-dynamic mode:
<system-config> <csp-enabled>true</csp-enabled> <csp-strict-dynamic-enabled>true</csp-strict-dynamic-enabled> <csp-policy>script-src 'self' 'nonce-{nonce}' 'strict-dynamic' https://www.google-analytics.com</csp-policy> </system-config>
- ZK replaces 'nonce-{nonce}' placeholder with actual generated nonce at runtime
- Custom policy completely replaces the default policy
- Developer is responsible for ensuring policy allows ZK scripts to execute
AC-1.5: Multiple CSP Directives Support (MEDIUM PRIORITY)
- Support multiple CSP directives beyond script-src
- Example with strict-dynamic:
<csp-policy> script-src 'self' 'nonce-{nonce}' 'strict-dynamic'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none' </csp-policy>
- Example with default mode:
<csp-policy> script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none' </csp-policy>
- Support style-src, img-src, font-src, connect-src, frame-ancestors, etc.
- Each directive should be configurable independently
AC-1.6: Advanced Custom Generator Configuration (MEDIUM PRIORITY)
- Support advanced use cases with custom CSP header generator class
<system-config> <csp-header-generator-class>my.company.CustomCspGenerator</csp-header-generator-class> </system-config>
- Custom generator class is optional for advanced scenarios
- When custom generator is provided, it overrides the default implementation
- Can be used with either default CSP or strict-dynamic mode
- Provides full control over CSP header generation and nonce management
AC-1.7: Report-Only Mode (HIGH PRIORITY)
- Support CSP report-only mode for testing without breaking application
<system-config> <csp-enabled>true</csp-enabled> <csp-strict-dynamic-enabled>true</csp-strict-dynamic-enabled> <csp-report-only>true</csp-report-only> <csp-report-uri>/csp-violations</csp-report-uri> </system-config>
- In report-only mode, violations are reported but not blocked
- Sends Content-Security-Policy-Report-Only header instead of Content-Security-Policy
- Essential for testing strict-dynamic mode before enforcing it
- Allows developers to identify violations without breaking application
2. Nonce Generation & Application
AC-2.1: Default Nonce Generator
- ZK provides default nonce generator implementation when strict-dynamic is enabled
- Nonce is only generated when csp-strict-dynamic-enabled=true
- When strict-dynamic is disabled, no nonce is generated (not needed for unsafe-inline/unsafe-eval mode)
- Nonce must be generated per page request (not reused between documents)
- Nonce must use cryptographically secure random number generator
- Nonce format requirements:
- Base64-encoded
- Minimum 128-bit entropy (recommended: 256-bit)
- Unique across concurrent requests
AC-2.2: Nonce Application Scope (HIGH PRIORITY)
When strict-dynamic is enabled (csp-strict-dynamic-enabled=true), ALL ZK-generated scripts must include nonce attribute:
- <zk:script> component
- <script> directive in ZUL
- Scripts from load-addon.xml
- <embed> in zk.xml
- Server-side generated scripts
- Client-side AU (AJAX Update) response scripts
- Widget instantiation scripts ($define)
- Event listener scripts
- Data binding scripts
- Dynamically created scripts via WebSocket/Server Push
When strict-dynamic is disabled (default mode), nonce attributes should not be added to scripts
AC-2.3: Developer API for Nonce Access (HIGH PRIORITY)
- Provide API for developers to access current nonce for their own script tags
- API should return null or empty string when strict-dynamic is disabled
- Java API:
String nonce = Executions.getCurrent().getNonce(); // or String nonce = CSPUtil.getNonce(execution); // Returns null when csp-strict-dynamic-enabled=false
- EL Expression support:
<script nonce="${execution.cspNonce}"> // custom script </script>
- Developer must only use nonce when strict-dynamic is enabled
Details
- Attached: POC project
Note: the POC is a early "hard-coded" implementation, and not representative of the intended state. - strict content detailed explanation
https://content-security-policy.com/strict-dynamic/ - useful tool to evaluate if a CSP policy is valid
https://csp-evaluator.withgoogle.com/ - user demand is high
Another idea
PageRenderer --> HtmlPageRenders
Because PageRender is defined in lang.xml and cannot be changed.
Provide a config to change HtmlPageRenders
- relates to
-
ZK-3813 improve CSP header support
-
- Closed
-