* Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2013 Juanjo Menent * Copyright (C) 2024 Frédéric France * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * \file htdocs/admin/security_headers_http.php * \ingroup core * \brief Security options setup */ // Load Dolibarr environment require '../main.inc.php'; /** * @var Conf $conf * @var DoliDB $db * @var HookManager $hookmanager * @var Translate $langs * @var User $user */ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; // Load translation files required by the page $langs->loadLangs(array("users", "admin", "other", "website")); if (!$user->admin) { accessforbidden(); } $action = GETPOST('action', 'aZ09'); $cancel = GETPOST('cancel', 'aZ09'); $forceCSP = getDolGlobalString("MAIN_SECURITY_FORCECSP"); $selectarrayCSPDirectives = GetContentPolicyDirectives(); $selectarrayCSPSources = GetContentPolicySources(); $forceCSPArr = GetContentPolicyToArray($forceCSP); $error = 0; /* * Actions */ if ($cancel) { $action = ''; } $reg = array(); if (preg_match('/set_([a-z0-9_\-]+)/i', $action, $reg)) { $code = $reg[1]; $value = (GETPOST($code, 'alpha') ? GETPOST($code, 'alpha') : 1); if (dolibarr_set_const($db, $code, $value, 'chaine', 0, '', $conf->entity) > 0) { header("Location: ".$_SERVER["PHP_SELF"]); exit; } else { dol_print_error($db); } } elseif (preg_match('/del_([a-z0-9_\-]+)/i', $action, $reg)) { $code = $reg[1]; if (dolibarr_del_const($db, $code, $conf->entity) > 0) { header("Location: ".$_SERVER["PHP_SELF"]); exit; } else { dol_print_error($db); } } elseif ($action == 'removecspsource') { $db->begin(); $sourcetype = ""; $sourcecsp = explode("_", GETPOST("sourcecsp")); $directive = $sourcecsp[0]; $sourcekey = isset($sourcecsp[1]) ? $sourcecsp[1] : null; $sourcedata = isset($sourcecsp[2]) ? $sourcecsp[2] : null; $forceCSPArr = GetContentPolicyToArray($forceCSP); $directivesarray = GetContentPolicyDirectives(); $sourcesarray = GetContentPolicySources(); if (empty($directive)) { $error++; } if (!empty($directivesarray[$directive])) { $directivetype = (string) $directivesarray[$directive]["data-directivetype"]; if (isset($sourcekey)) { $sourcetype = $sourcesarray[$directivetype][$sourcekey]["data-sourcetype"]; } } $securityspstring = ""; if (!$error && !empty($forceCSPArr)) { if (isset($sourcekey) && !empty($forceCSPArr[$directive][$sourcekey])) { unset($forceCSPArr[$directive][$sourcekey]); } if (count($forceCSPArr[$directive]) == 0) { unset($forceCSPArr[$directive]); } foreach ($forceCSPArr as $directive => $sourcekeys) { if ($securityspstring != "") { $securityspstring .= "; "; } $sourcestring = ""; foreach ($sourcekeys as $key => $source) { $directivetype = $directivesarray[$directive]["data-directivetype"]; $sourcetype = $sourcesarray[$directivetype][$source]["data-sourcetype"]; if ($sourcetype == "quoted") { $sourcestring .= " '".$source."'"; } else { $sourcestring .= " ".$source; } } $securityspstring .= $directive . $sourcestring; } $res = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSP', $securityspstring, 'chaine', 0, '', $conf->entity); if ($res <= 0) { $error++; } } if (!$error) { $db->commit(); setEventMessages($langs->trans("MainSecurityPolicySucesfullyRemoved"), null, 'mesgs'); } else { $db->rollback(); setEventMessages($langs->trans("MainErrorRemovingSecurityPolicy"), null, 'errors'); } header("Location: ".$_SERVER["PHP_SELF"]); exit; } elseif ($action == "updateform" && GETPOST("btn_MAIN_SECURITY_FORCECSP")) { $directivecsp = GETPOST("select_identifier_MAIN_SECURITY_FORCECSP"); $sourcecsp = GETPOST("select_source_MAIN_SECURITY_FORCECSP"); $sourcedatacsp = GETPOST("input_data_MAIN_SECURITY_FORCECSP"); $sourcetype = ""; $forceCSPArr = GetContentPolicyToArray($forceCSP); $directivesarray = GetContentPolicyDirectives(); $sourcesarray = GetContentPolicySources(); if (empty($directivecsp)) { $error++; } if ($error || (!isset($sourcecsp) && $directivesarray[$directivecsp]["data-directivetype"] != "none")) { $error++; } if (!$error) { $directivetype = $directivesarray[$directivecsp]["data-directivetype"]; if (isset($sourcecsp)) { $sourcetype = $sourcesarray[$directivetype][$sourcecsp]["data-sourcetype"]; } $securityspstring = ""; if (isset($sourcetype) && $sourcetype == "data") { $forceCSPArr[$directivecsp][] = "data:".$sourcedatacsp; } elseif (isset($sourcetype) && $sourcetype == "input") { if (empty($forceCSPArr[$directivecsp])) { $forceCSPArr[$directivecsp] = array(); } $forceCSPArr[$directivecsp] = array_merge(explode(" ", $sourcedatacsp), $forceCSPArr[$directivecsp]); } else { if (empty($forceCSPArr[$directivecsp])) { $forceCSPArr[$directivecsp] = array(); } if (!isset($sourcecsp)) { $sourcecsp = ""; } array_unshift($forceCSPArr[$directivecsp], $sourcecsp); } foreach ($forceCSPArr as $directive => $sourcekeys) { if ($securityspstring != "") { $securityspstring .= "; "; } $sourcestring = ""; foreach ($sourcekeys as $key => $source) { $directivetype = $directivesarray[$directive]["data-directivetype"]; $sourcetype = $sourcesarray[$directivetype][$source]["data-sourcetype"]; if (isset($sourcetype) && $sourcetype == "quoted") { $sourcestring .= " '".$source."'"; } elseif ($directivetype != "none") { $sourcestring .= " ".$source; } } $securityspstring .= $directive . $sourcestring; } $res = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSP', $securityspstring, 'chaine', 0, '', $conf->entity); if ($res <= 0) { $error++; } } if (!$error) { $db->commit(); setEventMessages($langs->trans("MainSecurityPolicySucesfullyAdded"), null, 'mesgs'); } else { $db->rollback(); setEventMessages($langs->trans("MainErrorAddingSecurityPolicy"), null, 'errors'); } header("Location: ".$_SERVER["PHP_SELF"]); exit; } elseif ($action == "updateform") { $db->begin(); $res1 = $res2 = $res3 = $res4 = 0; $securityrp = GETPOST('MAIN_SECURITY_FORCERP', 'alpha'); $securitysts = GETPOST('MAIN_SECURITY_FORCESTS', 'alpha'); $securitypp = GETPOST('MAIN_SECURITY_FORCEPP', 'alpha'); $securitysp = GETPOST('MAIN_SECURITY_FORCECSP', 'alpha'); $securitycspro = GETPOST('MAIN_SECURITY_FORCECSPRO', 'alpha'); $res1 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCERP', $securityrp, 'chaine', 0, '', $conf->entity); $res2 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCESTS', $securitysts, 'chaine', 0, '', $conf->entity); $res3 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCEPP', $securitypp, 'chaine', 0, '', $conf->entity); $res4 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSP', $securitysp, 'chaine', 0, '', $conf->entity); $res5 = dolibarr_set_const($db, 'MAIN_SECURITY_FORCECSPRO', $securitycspro, 'chaine', 0, '', $conf->entity); if ($res1 >= 0 && $res2 >= 0 && $res3 >= 0 && $res4 >= 0 && $res5 >= 0) { $db->commit(); setEventMessages($langs->trans("Saved"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]); exit; } else { $db->rollback(); setEventMessages($langs->trans("ErrorSavingChanges"), null, 'errors'); } $action = ''; $forceCSP = getDolGlobalString("MAIN_SECURITY_FORCECSP"); } /* * View */ $form = new Form($db); $wikihelp = 'EN:Setup_Security|FR:Paramétrage_Sécurité|ES:Configuración_Seguridad'; llxHeader('', $langs->trans("MainHttpSecurityHeaders"), $wikihelp, '', 0, 0, '', '', '', 'mod-admin page-security_other'); print load_fiche_titre($langs->trans("SecuritySetup"), '', 'title_setup'); $head = security_prepare_head(); print dol_get_fiche_head($head, 'headers_http', '', -1); print '
'; print ''.$langs->trans("HTTPHeaderEditor").'. '.$langs->trans("ReservedToAdvancedUsers").'.

'; print '
'; print ''; print ''; print '
'; print ''; print ''; print ''; print ''."\n"; print ''; // Force RP print ''; print ''; print ''; print ''; // Force STS print ''; print ''; print ''; print ''; // Force PP print ''; print ''; print ''; print ''; $examplecsprule = "frame-ancestors 'self'; img-src * data:; font-src *; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com;"; // Force CSP - Content Security Policy print ''; print ''; print ''; print ''; // Force CSPRO if (getDolGlobalString("MAIN_SECURITY_FORCECSPRO")) { print ''; print ''; print ''; print ''; } print '
'.$langs->trans("HTTPHeader").'
'.$form->textwithpicto($langs->trans('MainSecurityForceRP'), 'HTTP Header Referer-Policy

'.$langs->trans("Recommended").':
strict-origin-when-cross-origin   '.$langs->trans("or").'   same-origin (more secured)', 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCERP').'
'.$form->textwithpicto($langs->trans('MainSecurityForceSTS'), 'HTTP Header Strict-Transport-Security

'.$langs->trans("Example").':
max-age=31536000; includeSubDomains', 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCESTS').'
'.$form->textwithpicto($langs->trans('MainSecurityForcePP'), 'HTTP Header Permissions-Policy

'.$langs->trans("Example").':
camera=*, microphone=(), geolocation=*', 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCEPP').'
'.$form->textwithpicto($langs->trans('MainContentSecurityPolicy'), 'HTTP Header Content-Security-Policy

'.$langs->trans("Example").":
".$examplecsprule, 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCECSP').'
'; print '
'; print ' '.img_picto('', 'add').'
'; print ''; print ''; if (!empty($forceCSP)) { // Content Security Policy list of selected rules print '
'; print '
'; print img_picto('', 'graph', 'class="pictofixedwidth"').$langs->trans("HierarchicView").'
'; print '
    '; foreach ($forceCSPArr as $directive => $sources) { print '
  • '; if (in_array($directive, array_keys($selectarrayCSPDirectives))) { print ''.$directive.''; } else { print $form->textwithpicto($directive, $langs->trans("UnknowContentSecurityPolicyDirective"), 1, 'warning'); } if (!empty($sources)) { print '
      '; foreach ($sources as $key => $source) { print '
    • '.$source.' '.img_delete().'
    • '; } print '
    '; } else { print ' '.img_delete().''; } print '
  • '; } print '
'; print '
'; } print '
'; print '
'.$form->textwithpicto($langs->trans('MainSecurityForceCSPRO'), 'HTTP Header Content-Security-Policy-Report-Only

'.$langs->trans("Example").":
".$examplecsprule, 1, 'help', 'valignmiddle', 0, 3, 'MAIN_SECURITY_FORCECSPRO').'
'; print '
'; print '
'; print ''; print ''; print '
'; print ''; print '
'; print dol_get_fiche_end(); print ''; // End of page llxFooter(); $db->close();