Restrictive Content-Security-Policy for media #1045
This adds a CSP header for all media delivered through our fetch.php dispatcher. This should revent any scripts etc. to be executed when scriptable media, like SVG is used. Suggestions on finetuning the policy are welcome. The policy is added to the MEDIA_SENDFILE event, so plugins can easily influence it. The way it is passed as an array should make it easier to modify from plugins as well. I put the mechanism to send the header into it's own class in the HTTP namespace. Additional methods from inc/httputils could be moved here later. The method might also be interesting for #2198 and #1676.
This commit is contained in:
parent
a7e2efd2e2
commit
6cda96e3cf
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace dokuwiki\HTTP;
|
||||
|
||||
/**
|
||||
* Utilities to send HTTP Headers
|
||||
*/
|
||||
class Headers
|
||||
{
|
||||
/**
|
||||
* Send a Content-Security-Polica Header
|
||||
*
|
||||
* Expects an associative array with individual policies and their values
|
||||
*
|
||||
* @param array $policy
|
||||
*/
|
||||
static public function contentSecurityPolicy($policy)
|
||||
{
|
||||
foreach ($policy as $key => $values) {
|
||||
// if the value is not an array, we also accept newline terminated strings
|
||||
if (!is_array($values)) $values = explode("\n", $values);
|
||||
$values = array_map('trim', $values);
|
||||
$values = array_unique($values);
|
||||
$values = array_filter($values);
|
||||
$policy[$key] = $values;
|
||||
}
|
||||
|
||||
$cspheader = 'Content-Security-Policy:';
|
||||
foreach ($policy as $key => $values) {
|
||||
if ($values) {
|
||||
$cspheader .= " $key " . join(' ', $values) . ';';
|
||||
} else {
|
||||
$cspheader .= " $key;";
|
||||
}
|
||||
}
|
||||
|
||||
header($cspheader);
|
||||
}
|
||||
}
|
|
@ -13,22 +13,26 @@
|
|||
* This function will abort the current script when a 304 is sent or file sending is handled
|
||||
* through x-sendfile
|
||||
*
|
||||
* @param string $file local file to send
|
||||
* @param string $mime mime type of the file
|
||||
* @param bool $dl set to true to force a browser download
|
||||
* @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for no-cache)
|
||||
* @param bool $public is this a public ressource or a private one?
|
||||
* @param string $orig original file to send - the file name will be used for the Content-Disposition
|
||||
* @param array $csp The ContentSecurityPolicy to send
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
* @author Gerry Weissbach <dokuwiki@gammaproduction.de>
|
||||
*
|
||||
* @param string $file local file to send
|
||||
* @param string $mime mime type of the file
|
||||
* @param bool $dl set to true to force a browser download
|
||||
* @param int $cache remaining cache time in seconds (-1 for $conf['cache'], 0 for no-cache)
|
||||
* @param bool $public is this a public ressource or a private one?
|
||||
* @param string $orig original file to send - the file name will be used for the Content-Disposition
|
||||
*/
|
||||
function sendFile($file, $mime, $dl, $cache, $public = false, $orig = null) {
|
||||
function sendFile($file, $mime, $dl, $cache, $public = false, $orig = null, $csp=[]) {
|
||||
global $conf;
|
||||
// send mime headers
|
||||
header("Content-Type: $mime");
|
||||
|
||||
// send security policy if given
|
||||
if ($csp) dokuwiki\HTTP\Headers::contentSecurityPolicy($csp);
|
||||
|
||||
// calculate cache times
|
||||
if($cache == -1) {
|
||||
$maxage = max($conf['cachetime'], 3600); // cachetime or one hour
|
||||
|
|
|
@ -55,6 +55,15 @@ if (defined('SIMPLE_TEST')) {
|
|||
'status' => $STATUS,
|
||||
'statusmessage' => $STATUSMESSAGE,
|
||||
'ispublic' => media_ispublic($MEDIA),
|
||||
'csp' => [
|
||||
'sandbox' => '',
|
||||
'default-src' => "'none'",
|
||||
'script-src' => "'none'",
|
||||
'style-src' => "'unsafe-inline'",
|
||||
'media-src' => "'self'",
|
||||
'object-src' => "'self'",
|
||||
'form-action' => "'none'",
|
||||
],
|
||||
);
|
||||
|
||||
// handle the file status
|
||||
|
@ -96,7 +105,15 @@ if (defined('SIMPLE_TEST')) {
|
|||
// finally send the file to the client
|
||||
$evt = new Event('MEDIA_SENDFILE', $data);
|
||||
if($evt->advise_before()) {
|
||||
sendFile($data['file'], $data['mime'], $data['download'], $data['cache'], $data['ispublic'], $data['orig']);
|
||||
sendFile(
|
||||
$data['file'],
|
||||
$data['mime'],
|
||||
$data['download'],
|
||||
$data['cache'],
|
||||
$data['ispublic'],
|
||||
$data['orig'],
|
||||
$data['csp']
|
||||
);
|
||||
}
|
||||
// Do something after the download finished.
|
||||
$evt->advise_after(); // will not be emitted on 304 or x-sendfile
|
||||
|
|
Loading…
Reference in New Issue