[TikiWiki-commits] [Git][tikiwiki/tiki][master] [ENH] Expand HTTP Security Headers with Full CORS and Cross-Origin Policy Support

"SoftStart Code \(@softstartcode\) via TikiWiki-cvs" <[email protected]>
Newsgroups gmane.comp.cms.tiki.cvs
Message-ID <68d3f504e0b48_2cdf368958da@gitlab-sidekiq-low-urgency-cpu-bound-v2-69d75bf97c-pjxtt.mail>

SoftStart Code pushed to branch master at Tiki Wiki CMS Groupware / Tiki


Commits:
a053fbdf by Sandeep D at 2025-09-24T13:31:14+00:00
[ENH] Expand HTTP Security Headers with Full CORS and Cross-Origin Policy Support
---
* [ENH] Expand HTTP Security Headers with Full CORS and Cross-Origin Policy Support

Enhance Tiki's security headers to protect against cross-origin vulnerabilities and improve CORS support.

**New CORS Headers:**
- `Access-Control-Allow-Credentials` - Controls whether credentials can be included in cross-origin requests
- `Access-Control-Allow-Methods` - Specifies allowed HTTP methods for cross-origin requests
- `Access-Control-Allow-Headers` - Specifies allowed headers for cross-origin requests

**New Cross-Origin Policy Headers:**
- `Cross-Origin-Resource-Policy` - Controls which cross-origin requests can access resources
- `Cross-Origin-Embedder-Policy` - Controls loading of cross-origin resources in documents
- `Cross-Origin-Opener-Policy` - Controls interaction with other browsing contexts

**Features:**
- **Admin Panel Integration**: All headers configurable via `tiki-admin.php?page=security` → "General Security" tab
- **Smart Validation**: Prevents CORS wildcard conflicts when credentials are enabled
- **MDN Compliance**: All values follow official MDN specifications with proper validation
- **Safe Defaults**: All headers disabled by default for backward compatibility
- **Comprehensive Documentation**: Each header includes detailed descriptions and MDN help links

**Technical Implementation:**
- Headers automatically injected into all HTTP responses via `SmartyTiki.php`
- Preference system with proper dependencies and conditional UI
- Error handling for invalid CORS configurations
- Integration with existing `Access-Control-Allow-Origin` implementation

See merge request tikiwiki/tiki!8639

- - - - -


3 changed files:

- lib/core/Tiki/Smarty/SmartyTiki.php
- lib/prefs/http.php
- templates/admin/include_security.tpl


Changes:

=====================================
lib/core/Tiki/Smarty/SmartyTiki.php
=====================================
@@ -285,7 +285,36 @@ class SmartyTiki extends Smarty
             } else {
                 $content_type_options = $prefs['http_header_content_type_options'];
             }
-
+            if (! isset($prefs['http_header_access_control_allow_credentials'])) {
+                $access_control_allow_credentials = false;
+            } else {
+                $access_control_allow_credentials = $prefs['http_header_access_control_allow_credentials'];
+            }
+            if (! isset($prefs['http_header_access_control_allow_methods'])) {
+                $access_control_allow_methods = false;
+            } else {
+                $access_control_allow_methods = $prefs['http_header_access_control_allow_methods'];
+            }
+            if (! isset($prefs['http_header_access_control_allow_headers'])) {
+                $access_control_allow_headers = false;
+            } else {
+                $access_control_allow_headers = $prefs['http_header_access_control_allow_headers'];
+            }
+            if (! isset($prefs['http_header_cross_origin_embedder_policy'])) {
+                $cross_origin_embedder_policy = false;
+            } else {
+                $cross_origin_embedder_policy = $prefs['http_header_cross_origin_embedder_policy'];
+            }
+            if (! isset($prefs['http_header_cross_origin_resource_policy'])) {
+                $cross_origin_resource_policy = false;
+            } else {
+                $cross_origin_resource_policy = $prefs['http_header_cross_origin_resource_policy'];
+            }
+            if (! isset($prefs['http_header_cross_origin_opener_policy'])) {
+                $cross_origin_opener_policy = false;
+            } else {
+                $cross_origin_opener_policy = $prefs['http_header_cross_origin_opener_policy'];
+            }
             if (! isset($prefs['http_header_content_security_policy'])) {
                 $content_security_policy = false;  // prevent smarty E_NOTICE
             } else {
@@ -315,6 +344,73 @@ class SmartyTiki extends Smarty
             if ($content_type_options == 'y') {
                 header('X-Content-Type-Options: nosniff');
             }
+            if ($access_control_allow_credentials === 'y') {
+                header('Access-Control-Allow-Credentials: true');
+            }
+            if ($access_control_allow_methods === 'y') {
+                $header_value = trim($prefs['http_header_access_control_allow_methods_value']);
+                if ($access_control_allow_credentials === 'y' && $header_value === '*') {
+                    Feedback::error(tr("CORS configuration error: Wildcard (*) is not allowed for Access-Control-Allow-Methods when http_header_access_control_allow_credential is enabled"));
+                    header_remove('Access-Control-Allow-Methods');
+                } else {
+                    header('Access-Control-Allow-Methods: ' . $header_value);
+                }
+            }
+            if ($access_control_allow_headers === 'y') {
+                $headers_value = trim($prefs['http_header_access_control_allow_headers_value']);
+                if ($access_control_allow_credentials === 'y' && $headers_value === '*') {
+                    Feedback::error(tr("CORS configuration error: Wildcard (*) is not allowed for Access-Control-Allow-Headers when Access-Control-Allow-Credentials is enabled."));
+                    header_remove('Access-Control-Allow-Headers');
+                } else {
+                    header('Access-Control-Allow-Headers: ' . $headers_value);
+                }
+            }
+            if ($cross_origin_embedder_policy === 'y') {
+                switch (trim($prefs['http_header_cross_origin_embedder_policy_value'])) {
+                    case 'require-corp':
+                        header('Cross-Origin-Embedder-Policy: require-corp');
+                        break;
+                    case 'credentialless':
+                        header('Cross-Origin-Embedder-Policy: credentialless');
+                        break;
+                    case 'unsafe-none':
+                    default:
+                        header_remove('Cross-Origin-Embedder-Policy');
+                        break;
+                }
+            }
+            if ($cross_origin_resource_policy === 'y') {
+                switch (trim($prefs['http_header_cross_origin_resource_policy_value'])) {
+                    case 'same-origin':
+                        header('Cross-Origin-Resource-Policy: same-origin');
+                        break;
+                    case 'same-site':
+                        header('Cross-Origin-Resource-Policy: same-site');
+                        break;
+                    case 'cross-origin':
+                        header('Cross-Origin-Resource-Policy: cross-origin');
+                        break;
+                    default:
+                        break;
+                }
+            }
+            if ($cross_origin_opener_policy === 'y') {
+                switch (trim($prefs['http_header_cross_origin_opener_policy_value'])) {
+                    case 'same-origin':
+                        header('Cross-Origin-Opener-Policy: same-origin');
+                        break;
+                    case 'same-origin-allow-popups':
+                        header('Cross-Origin-Opener-Policy: same-origin-allow-popups');
+                        break;
+                    case 'same-origin-plus-coep':
+                        header('Cross-Origin-Opener-Policy: same-origin-plus-coep');
+                        break;
+                    case 'unsafe-none':
+                    default:
+                        header_remove('Cross-Origin-Opener-Policy');
+                        break;
+                }
+            }
             if ($content_security_policy == 'y') {
                 $header_value = $prefs['http_header_content_security_policy_value'];
                 header('Content-Security-Policy: ' . $header_value);


=====================================
lib/prefs/http.php
=====================================
@@ -50,6 +50,14 @@ function prefs_http_list()
                 'http_header_frame_options',
             ],
         ],
+        'http_header_access_control_allow_credentials' => [
+            'name' => tra('HTTP header allow credentials'),
+            'description' => tra('The Access-Control-Allow-Credentials response header tells browsers whether the server allows cross-origin HTTP requests to include credentials.'),
+            'type' => 'flag',
+            'default' => 'n',
+            'perspective' => false,
+            'tags' => ['advanced']
+        ],
         'http_header_xss_protection' => [
             'name' => tra('HTTP header x-xss-protection'),
             'description' => tra('The x-xss-protection header is designed to enable the cross-site scripting (XSS) filter built into modern web browsers'),
@@ -73,6 +81,72 @@ function prefs_http_list()
                 'http_header_xss_protection',
             ],
         ],
+        'http_header_cross_origin_embedder_policy' => [
+            'name' => tra('HTTP header cross-origin-embedder-policy'),
+            'description' => tra('Controls the loading of cross-origin resources in a document. Setting this header helps enhance security by ensuring that loaded resources explicitly grant permission to be loaded.'),
+            'type' => 'flag',
+            'default' => 'n',
+            'perspective' => false,
+            'tags' => ['advanced'],
+        ],
+        'http_header_cross_origin_embedder_policy_value' => [
+            'name' => tra('Header value'),
+            'description' => tra('Specifies the policy for loading cross-origin resources. "Require-CORP" requires cross-origin resources to have CORP headers. "Credentialless" allows loading cross-origin resources without credentials. "Unsafe-none" applies no restrictions.'),
+            'type' => 'list',
+            'options' => [
+                'unsafe-none' => tra('None'),
+                'require-corp' => tra('Require-CORP'),
+                'credentialless' => tra('Credentialless'),
+            ],
+            'default' => '',
+            'perspective' => false,
+            'tags' => ['advanced'],
+        ],
+        'http_header_cross_origin_resource_policy' => [
+            'name' => tra('HTTP header Cross-Origin-Resource-Policy'),
+            'description' => tra('Defines which cross-origin requests are allowed to access resources on your site. This header can help prevent other sites from reading or loading your site\'s resources without permission.'),
+            'type' => 'flag',
+            'default' => 'n',
+            'perspective' => false,
+            'tags' => ['advanced'],
+        ],
+        'http_header_cross_origin_resource_policy_value' => [
+            'name' => tra('Header value'),
+            'description' => tra('Determines which origins are allowed to access resources. "Same-Origin" only allows your own site to access resources. "Same-Site" extends this to your entire site, including subdomains. "Cross-Origin" allows any site to access the resources.'),
+            'type' => 'list',
+            'options' => [
+                'same-origin' => tra('Same-Origin: Only same-origin requests are allowed.'),
+                'same-site' => tra('Same-Site: Only requests from the same site are allowed.'),
+                'cross-origin' => tra('Cross-Origin: Allows requests from any origin.'),
+            ],
+            'default' => '',
+            'perspective' => false,
+            'tags' => ['advanced'],
+            'help' => 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy',
+        ],
+        'http_header_cross_origin_opener_policy' => [
+            'name' => tra('HTTP header Cross-Origin-Opener-Policy'),
+            'description' => tra('Enables or disables the sending of the Cross-Origin-Opener-Policy header in HTTP responses from your site. This header controls how the document may interact with other browsing contexts.'),
+            'type' => 'flag',
+            'default' => 'n',
+            'perspective' => false,
+            'tags' => ['advanced'],
+        ],
+        'http_header_cross_origin_opener_policy_value' => [
+            'name' => tra('Header value'),
+            'description' => tra('Specifies the policy for cross-origin opener policy header.'),
+            'type' => 'list',
+            'options' => [
+                'same-origin' => tra('Same-Origin: Allows the document to be opened only by pages from the same origin.'),
+                'same-origin-allow-popups' => tra('Same-Origin-Allow-Popups: Allows the document to be opened by pages from the same origin, and allows those pages to open popups.'),
+                'same-origin-plus-coep' => tra('Same-Origin-Plus-COEP: Allows the document to be opened only by pages from the same origin, and sets the Cross-Origin-Embedder-Policy header to `require-corp`.'),
+                'unsafe-none' => tra('None: No specific policy is set.'),
+            ],
+            'default' => '',
+            'perspective' => false,
+            'tags' => ['advanced'],
+            'help' => 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy',
+        ],
         'http_header_content_type_options' => [
             'name' => tra('HTTP header x-content-type-options'),
             'description' => tra('The x-content-type-options header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed.'),
@@ -104,6 +178,52 @@ function prefs_http_list()
             ),
             'help' => 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy',
         ],
+        'http_header_access_control_allow_methods' => [
+            'name' => tra('HTTP header access-control-allow-methods'),
+            'description' => tra('Enables or disables the sending of the Access-Control-Allow-Methods header in HTTP responses from your Tiki site. This header is crucial for Cross-Origin Resource Sharing (CORS) and specifies the HTTP methods that are allowed when accessing resources in response to a preflight request.'),
+            'type' => 'flag',
+            'default' => 'n',
+            'perspective' => false,
+            'tags' => ['advanced'],
+        ],
+        'http_header_access_control_allow_methods_value' => [
+            'name' => tra('Header value'),
+            'type' => 'text',
+            'default' => '',
+            'perspective' => false,
+            'tags' => ['advanced'],
+            'dependencies' => [
+                'http_header_access_control_allow_methods',
+            ],
+            'description' => tr(
+                'Specifies the HTTP methods that are allowed for cross-origin requests. This setting takes effect only if the HTTP header access-control-allow-methods is enabled. Separate multiple methods with commas. For example, to allow GET, POST, and PUT methods, set this value to %0',
+                '<code>GET, POST, PUT</code>'
+            ),
+            'help' => 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods',
+        ],
+        'http_header_access_control_allow_headers' => [
+            'name' => tra('HTTP header access-control-allow-headers'),
+            'description' => tra('Enables or disables the sending of the Access-Control-Allow-Headers header in HTTP responses from your Tiki site. This header is crucial for Cross-Origin Resource Sharing (CORS) and specifies the headers that are allowed when making actual requests (after the preflight has been accepted).'),
+            'type' => 'flag',
+            'default' => 'n',
+            'perspective' => false,
+            'tags' => ['advanced'],
+        ],
+        'http_header_access_control_allow_headers_value' => [
+            'name' => tra('Header value'),
+            'type' => 'text',
+            'default' => '',
+            'perspective' => false,
+            'tags' => ['advanced'],
+            'dependencies' => [
+                'http_header_access_control_allow_headers',
+            ],
+            'description' => tr(
+                'Specifies the HTTP headers that can be used when making the actual request. This setting takes effect only if the HTTP header access-control-allow-headers is enabled. Separate multiple header names with commas. For example, to allow headers such as Content-Type, Accept, and X-Requested-With, set this value to %0',
+                '<code>Content-Type, Accept, X-Requested-With</code>'
+            ),
+            'help' => 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers',
+        ],
         'http_header_strict_transport_security' => [
             'name' => tra('HTTP header strict-transport-security'),
             'description' => tra('The Strict-Transport-Security header (often abbreviated as HSTS) is a security feature that lets a web site tell browsers that it should only be communicated with using HTTPS, instead of using HTTP.'),


=====================================
templates/admin/include_security.tpl
=====================================
@@ -143,6 +143,33 @@
 
                 {preference name=http_header_content_type_options}
 
+                {preference name=http_header_access_control_allow_credentials}
+
+                {preference name=http_header_access_control_allow_methods}
+                <div class="adminoptionboxchild" id="http_header_access_control_allow_methods_childcontainer">
+                    {preference name=http_header_access_control_allow_methods_value}
+                </div>
+
+                {preference name=http_header_access_control_allow_headers}
+                <div class="adminoptionboxchild" id="http_header_access_control_allow_headers_childcontainer">
+                    {preference name=http_header_access_control_allow_headers_value}
+                </div>
+
+                {preference name=http_header_cross_origin_embedder_policy}
+                <div class="adminoptionboxchild" id="http_header_cross_origin_embedder_policy_childcontainer">
+                    {preference name=http_header_cross_origin_embedder_policy_value}
+                </div>
+
+                {preference name=http_header_cross_origin_resource_policy}
+                <div class="adminoptionboxchild" id="http_header_cross_origin_resource_policy_childcontainer">
+                    {preference name=http_header_cross_origin_resource_policy_value}
+                </div>
+
+                {preference name=http_header_cross_origin_opener_policy}
+                <div class="adminoptionboxchild" id="http_header_cross_origin_opener_policy_childcontainer">
+                    {preference name=http_header_cross_origin_opener_policy_value}
+                </div>
+
                 {preference name=http_header_content_security_policy}
                 <div class="adminoptionboxchild" id="http_header_content_security_policy_childcontainer">
                     {preference name=http_header_content_security_policy_value}



View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/a053fbdf11254b9aaaa398e7e11194f06a5369d4

-- 
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/a053fbdf11254b9aaaa398e7e11194f06a5369d4
You're receiving this email because of your account on gitlab.com.

_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.