From 067beedf2917dfe58abc9e306ac69951bdb48e34 Mon Sep 17 00:00:00 2001 From: Joshua Ramon Enslin Date: Mon, 18 Jun 2018 13:57:35 +0200 Subject: [PATCH] Improved settings of CSPs. Added manifest.json. Added further security-related HTTP headers. --- edit/editHTMLPage.php | 2 +- edit/inc/standardHTML.php | 23 +++++++--- edit/index.php | 2 +- edit/page.php | 2 +- edit/pages.php | 2 +- edit/settings.php | 74 ++++++++++++++++++++++++--------- edit/themes/default/default.css | 2 + edit/translations/en.php | 26 ++++++++++++ edit/users.php | 2 +- inc/functions.php | 13 +++++- inc/standardHTML.php | 11 ++++- manifest.php | 40 ++++++++++++++++++ 12 files changed, 165 insertions(+), 34 deletions(-) create mode 100644 manifest.php diff --git a/edit/editHTMLPage.php b/edit/editHTMLPage.php index 211ed4c..fbd7870 100644 --- a/edit/editHTMLPage.php +++ b/edit/editHTMLPage.php @@ -59,7 +59,7 @@ if (isset($task) and $task == "update") { if (!isset($public)) $public = false; -echo printBackendHead($pageTitle); +echo printBackendHead($settings, $pageTitle, $pageTitle, $settings['logo']); echo printBackendHeader($pageTitle, $translations["help$id"]); echo ' diff --git a/edit/inc/standardHTML.php b/edit/inc/standardHTML.php index 0292085..4e985d2 100644 --- a/edit/inc/standardHTML.php +++ b/edit/inc/standardHTML.php @@ -1,26 +1,37 @@ */ /** * Prints the head element of an HTML page * - * @param string $page Name / ID of the current page. - * @param string $title Title of the page. - * @param string $icon The icon of the website. + * @param array $settings Settings variable. + * @param string $page Name / ID of the current page. + * @param string $title Title of the page. + * @param string $icon The icon of the website. * * @return string */ -function printBackendHead(string $page = "home", string $title = "Home", string $icon = ""):string { +function printBackendHead(array $settings, string $page = "home", string $title = "Home", string $icon = ""):string { $output = ' - + ' . $title . ' @@ -29,7 +40,7 @@ function printBackendHead(string $page = "home", string $title = "Home", string if ($icon) { $output .= ' - + '; } diff --git a/edit/index.php b/edit/index.php index 6bdf0b7..6038241 100644 --- a/edit/index.php +++ b/edit/index.php @@ -25,7 +25,7 @@ $pages = loadPages(); // Load overview of pages. * Output */ -echo printBackendHead($translations['start'], $translations['start'], $settings['logo']); +echo printBackendHead($settings, $translations['start'], $translations['start'], $settings['logo']); echo printBackendHeader($translations['start'], $translations['helpStart']); echo ' diff --git a/edit/page.php b/edit/page.php index ac49142..2326faa 100644 --- a/edit/page.php +++ b/edit/page.php @@ -75,7 +75,7 @@ if (isset($task)) { if (!isset($public)) $public = false; -echo printBackendHead($pageTitle, $pageTitle, $settings['logo']); +echo printBackendHead($settings, $pageTitle, $pageTitle, $settings['logo']); echo printBackendHeader($pageTitle, $translations['helpSinglePage']); echo ' diff --git a/edit/pages.php b/edit/pages.php index 68e55f9..29dc819 100644 --- a/edit/pages.php +++ b/edit/pages.php @@ -25,7 +25,7 @@ $pages = loadPages(); // Load overview of pages. * Output */ -echo printBackendHead($translations['pagesOverview'], $translations['pagesOverview'], $settings['logo']); +echo printBackendHead($settings, $translations['pagesOverview'], $translations['pagesOverview'], $settings['logo']); echo printBackendHeader($translations['pagesOverview'], $translations['helpPagesOverview']); echo ' diff --git a/edit/settings.php b/edit/settings.php index 5b9871c..835325a 100644 --- a/edit/settings.php +++ b/edit/settings.php @@ -22,7 +22,7 @@ $pages = loadPages(); // Load overview of pages. */ // Check for vars. -loadHttpToGlobals(["task", "startPage", "pageTitle", "logo", "url", "css", "hideInstitution", "mdVersion", "mdImgFolder", "cacheRefreshInterval", "limitToInstitutions", "maxFileSize", "defaultLang"]); +loadHttpToGlobals(["task", "startPage", "pageTitle", "logo", "url", "css", "hideInstitution", "mdVersion", "mdImgFolder", "cacheRefreshInterval", "limitToInstitutions", "maxFileSize", "sendHTTPHeaders", "CSPimageSources", "CSPobjectSources", "defaultLang"]); if (isset($task) and $task == "update") { // Adding new users. @@ -36,7 +36,7 @@ if (isset($task) and $task == "update") { // Adding new users. if (isset($mdImgFolder)) $mdImgFolder = rtrim($mdImgFolder, "/") . "/"; if (isset($limitToInstitutions)) $settings['limitToInstitutions'] = array_diff(explode(',', $limitToInstitutions), ['']); - foreach (["startPage", "pageTitle", "logo", "url", "css", "hideInstitution", "mdVersion", "mdImgFolder", "cacheRefreshInterval", "maxFileSize", "defaultLang"] as $var) { + foreach (["startPage", "pageTitle", "logo", "url", "css", "hideInstitution", "mdVersion", "mdImgFolder", "cacheRefreshInterval", "maxFileSize", "sendHTTPHeaders", "CSPimageSources", "CSPobjectSources", "defaultLang"] as $var) { if (isset($$var)) $settings[$var] = $$var; } @@ -53,7 +53,7 @@ if (isset($task) and $task == "update") { // Adding new users. * Output */ -echo printBackendHead($translations['settings'], $translations['settings'], $settings['logo']); +echo printBackendHead($settings, $translations['settings'], $translations['settings'], $settings['logo']); echo printBackendHeader($translations['settings'], $translations['helpSettings']); echo ' @@ -70,6 +70,10 @@ echo '
+ + + + @@ -117,6 +121,7 @@ echo ' - - + - + + + + + @@ -164,6 +181,19 @@ echo ' + + + + + + + + + + + @@ -171,21 +201,27 @@ echo ' + - + - + + + + + + + + + + + + + + + diff --git a/edit/themes/default/default.css b/edit/themes/default/default.css index 735226f..7533558 100644 --- a/edit/themes/default/default.css +++ b/edit/themes/default/default.css @@ -109,6 +109,8 @@ table.obj_cha_maintable button:only-child, table.obj_cha_maintable input:only-child { width: 100%; } table.obj_cha_maintable textarea:only-child { width: 100%; height: 14em; } +table.obj_cha_maintable th.sectionTH { padding: 1em 0; font-size: 1.1em; font-weight: bold; } +table.obj_cha_maintable > tbody > tr:first-child th.sectionTH { padding: 0 0 1em 0; } table.obj_cha_maintable input[type="range"]:only-child { margin: .5em -1em 0 0; } @media screen and (min-width:65em) { diff --git a/edit/translations/en.php b/edit/translations/en.php index e02c6ab..9ba98c3 100644 --- a/edit/translations/en.php +++ b/edit/translations/en.php @@ -16,6 +16,9 @@ $translations = [ "preview" => "Preview", "banner" => "Banner", "delete" => "Delete", + "general" => "General Settings", + "security" => "Security", + "integrationWithMD" => "Integration with Museum-Digital", "languageUnavailable" => "This language is not available.", "settingsUpdated" => "Updated settings.", "staticPageTitle" => "Page title", @@ -41,6 +44,29 @@ $translations = [ "helpAdmin" => "

Is the user an administrator?

", "language" => "Language", "helpLanguage" => "

The default language of this instance of md:cms.

", + "sendHTTPHeaders" => "Send additional HTTP Headers", + "helpSendHTTPHeaders" => "

md:cms can send additional directives to the browser to increase security. Your server administrator can set these server wide, and if they have done so already, you should disable this option (this is by far the prefered way). In most cases, server administrators have not opted to do so yet, and keeping this option enabled makes sense. If you want to inquire into this further, the Mozilla Observatory is a useful resource.

+

+ The default headers sent are the following: +


+            X-Content-Type-Options: nosniff
+            X-XSS-Protection: 1; mode=block
+            Strict-Transport-Security: max-age=31536000; preload
+            Referrer-Policy: strict-origin
+        
+

", + "CSPimageSources" => "Image sources (whitelist)", + "helpCSPimageSources" => " +

To increase security, md:cms directs browsers to refrain from loading images from any but the whitelisted sources. By default, only the current domain and the linked instance of museum-digital are whitelisted.

+

If you, for example, want to embed images from flickr.com, you either need to download the images and upload them here, or you whitelist flickr. To whitelist domains, please enter them one by one into the field, separated by whitespaces. E.g.
+ https://www.flickr.com https://www.google.com +

", + "CSPobjectSources" => "Object sources (whitelist)", + "helpCSPobjectSources" => " +

To increase security, md:cms directs browsers to refrain from loading frames and objects from any but the whitelisted sources. By default, only the current domain and the linked instance of museum-digital are whitelisted.

+

If you, for example, want to embed videos from youtube.com, you need to whitelist youtube. To whitelist domains, please enter them one by one into the field, separated by whitespaces. E.g.
+ https://www.youtube.com https://www.vimeo.com +

", "maxFileSize" => "Maximum upload size", "helpMaxFileSize" => "

The maximum file size of file uploads.

", "cacheRefreshInterval" => "Cache Refresh Interval", diff --git a/edit/users.php b/edit/users.php index d8f0a11..bdfbeed 100644 --- a/edit/users.php +++ b/edit/users.php @@ -84,7 +84,7 @@ if (isset($task) and $task == "insert") { // Adding new users. * Output */ -echo printBackendHead($translations['start'], $translations['start'], $settings['logo']); +echo printBackendHead($settings, $translations['start'], $translations['start'], $settings['logo']); echo printBackendHeader($translations['usersOverview'], $translations['helpUsers']); echo ' diff --git a/inc/functions.php b/inc/functions.php index 2fafc08..1bb107e 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -74,19 +74,28 @@ function ensureEnvironment() { "logo" => "", "url" => "", "css" => "default", - "hideInstitution" => 0, + "defaultLang" => "en", "cacheRefreshInterval" => 0, "mdVersion" => "https://rlp.museum-digital.de/", "mdImgFolder" => "https://rlp.museum-digital.de/data/rlp/", + "hideInstitution" => 0, "limitToInstitutions" => [], + "sendHTTPHeaders" => 1, + "CSPimageSources" => "", + "CSPobjectSources" => "", "maxFileSize" => 300000, - "defaultLang" => "en" ], json_decode(file_get_contents(__DIR__ . "/../data/settings.json"), true) ); $GLOBALS['settings'] = $settings; + if ($settings['sendHTTPHeaders']) { + header('X-Content-Type-Options: nosniff'); + header('X-XSS-Protection: 1; mode=block'); + header('Strict-Transport-Security: max-age=31536000; preload'); + header('Referrer-Policy: strict-origin'); + } } diff --git a/inc/standardHTML.php b/inc/standardHTML.php index 70aeb89..f9c9a8a 100644 --- a/inc/standardHTML.php +++ b/inc/standardHTML.php @@ -25,11 +25,18 @@ function printPublicHead(array $settings, string $page = "home", string $title = - + ' . $title . ' - + + '; $output .= $additional; diff --git a/manifest.php b/manifest.php new file mode 100644 index 0000000..a0b7216 --- /dev/null +++ b/manifest.php @@ -0,0 +1,40 @@ + + */ + +// Include functions and settings. + +require_once __DIR__ . "/inc/functions.php"; + +// Ensure working environment for frontend. + +ensureEnvironment(); + +// Fill output array + +$data = []; + +$data["name"] = $data['short_name'] = $settings['pageTitle']; +$data["start_url"] = "/"; +$data["display"] = "standalone"; +$data["background_color"] = "#000"; +$data["theme_color"] = "#AFB42B"; +$data["description"] = "Website of " . $settings['pageTitle']; + +/* +$data['icons'] = [ + "src" => $settings['logo'], + "type" => mime_content_type(__DIR__ . $settings['logo']) +]; + */ + +// Return JSON-encoded data. + +header('Content-Type: application/json'); +echo json_encode($data, JSON_PRETTY_PRINT); + +?> +
' . $translations['general'] . '
' . generateHelpToolTip("helpSettingsUsedCSS", $translations['settingsUsedCSS'], $translations['helpSettingsUsedCSS']) . '
- + ' . generateHelpToolTip("helpHideInstitution", $translations['hideInstitution'], $translations['helpHideInstitution']) . '' . generateHelpToolTip("helpLanguage", $translations['language'], $translations['helpLanguage']) . '
' . $translations['integrationWithMD'] . '
' . generateHelpToolTip("helpLimitToInstitutions", $translations['limitToInstitutions'], $translations['helpLimitToInstitutions']) . '
+ + ' . generateHelpToolTip("helpHideInstitution", $translations['hideInstitution'], $translations['helpHideInstitution']) . '
' . $translations['security'] . '
' . generateHelpToolTip("helpMaxFileSize", $translations['maxFileSize'], $translations['helpMaxFileSize']) . '
- + ' . generateHelpToolTip("helpLanguage", $translations['language'], $translations['helpLanguage']) . '' . generateHelpToolTip("helpSendHTTPHeaders", $translations['sendHTTPHeaders'], $translations['helpSendHTTPHeaders']) . '
' . generateHelpToolTip("helpCSPimageSources", $translations['CSPimageSources'], $translations['helpCSPimageSources']) . '
' . generateHelpToolTip("helpCSPobjectSources", $translations['CSPobjectSources'], $translations['helpCSPobjectSources']) . '