Skip to content

Commit

Permalink
Fix weakened CSP by Gravatar plugin
Browse files Browse the repository at this point in the history
Merge vboctor's branch 'issue_21263_csp_headers_13x'

Fixes #21263
  • Loading branch information
dregad committed Aug 27, 2016
2 parents 77db038 + c13b325 commit b3511d2
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 23 deletions.
1 change: 1 addition & 0 deletions core.php
Expand Up @@ -274,6 +274,7 @@ function __autoload( $p_class ) {

# Set HTTP response headers
require_api( 'http_api.php' );
event_signal( 'EVENT_CORE_HEADERS' );
http_all_headers();

# Push default language to speed calls to lang_get
Expand Down
1 change: 1 addition & 0 deletions core/events_inc.php
Expand Up @@ -33,6 +33,7 @@

# Events specific to the core system
'EVENT_CORE_READY' => EVENT_TYPE_EXECUTE,
'EVENT_CORE_HEADERS' => EVENT_TYPE_EXECUTE,

# MantisBT Layout Events
'EVENT_LAYOUT_RESOURCES' => EVENT_TYPE_OUTPUT,
Expand Down
88 changes: 73 additions & 15 deletions core/http_api.php
Expand Up @@ -29,6 +29,12 @@

require_api( 'config_api.php' );

/**
* The Content-Security-Policy settings array. Use http_csp_add() to update it.
* @var array
*/
$g_csp = array();

/**
* Checks to see if script was queried through the HTTPS protocol
* @return boolean True if protocol is HTTPS
Expand Down Expand Up @@ -138,6 +144,64 @@ function http_content_headers() {
}
}

/**
* Add a Content-Security-Policy directive.
*
* @param string $p_type The directive type, e.g. style-src, script-src.
* @param string $p_value The directive value, e.g. 'self', https://ajax.googleapis.com
* @return void
*/
function http_csp_add( $p_type, $p_value ) {
global $g_csp;

if ( $g_csp === null ) {
# Development error, headers already emitted.
trigger_error( ERROR_GENERIC, ERROR );
}

if ( isset( $g_csp[$p_type] ) ) {
if ( !in_array( $p_value, $g_csp[$p_type] ) ) {
$g_csp[$p_type][] = $p_value;
}
} else {
$g_csp[$p_type] = array( $p_value );
}
}

/**
* Constructs the value of the CSP header.
* @return string CSP header value.
*/
function http_csp_value() {
global $g_csp;

if ( $g_csp === null ) {
# Development error, headers already emitted.
trigger_error( ERROR_GENERIC, ERROR );
}

$t_csp_value = '';

foreach ( $g_csp as $t_key => $t_values ) {
$t_csp_value .= $t_key . ' ' . implode( ' ', $t_values ) . '; ';
}

$t_csp_value = trim( $t_csp_value, '; ' );

return $t_csp_value;
}

/**
* Send header for Content-Security-Policy.
* @return void
*/
function http_csp_emit_header() {
header( 'Content-Security-Policy: ' . http_csp_value() );

global $g_csp;
$g_csp = null;
}

/**
* Set security headers (frame busting, clickjacking/XSS/CSRF protection).
* @return void
Expand All @@ -147,32 +211,26 @@ function http_security_headers() {
header( 'X-Frame-Options: DENY' );

# Define Content Security Policy
$t_csp = array(
"default-src 'self'",
"frame-ancestors 'none'",
);

$t_style_src = "style-src 'self'";
$t_script_src = "script-src 'self'";
http_csp_add( 'default-src', "'self'" );
http_csp_add( 'frame-ancestors', "'none'" );
http_csp_add( 'style-src', "'self'" );
http_csp_add( 'script-src', "'self'" );
http_csp_add( 'img-src', "'self'" );

# White list the CDN urls (if enabled)
if ( config_get_global( 'cdn_enabled' ) == ON ) {
$t_cdn_url = 'https://ajax.googleapis.com';
$t_style_src .= " $t_cdn_url";
$t_script_src .= " $t_cdn_url";
http_csp_add( 'style-src', $t_cdn_url );
http_csp_add( 'script-src', $t_cdn_url );
}

# Relaxing policy for roadmap page to allow inline styles
# This is a workaround to fix the broken progress bars (see #19501)
if( 'roadmap_page.php' == basename( $_SERVER['SCRIPT_NAME'] ) ) {
$t_style_src .= " 'unsafe-inline'";
http_csp_add( 'style-src', "'unsafe-inline'" );
}

$t_csp[] = $t_style_src;
$t_csp[] = $t_script_src;

# Set CSP header
header( 'Content-Security-Policy: ' . implode('; ', $t_csp) );
http_csp_emit_header();

if( http_is_protocol_https() ) {
header( 'Strict-Transport-Security: max-age=7776000' );
Expand Down
13 changes: 13 additions & 0 deletions docbook/Developers_Guide/en-US/Events_Reference.xml
Expand Up @@ -83,6 +83,19 @@
</blockquote>
</blockquote>

<blockquote id="dev.eventref.system.coreheaders">
<title>EVENT_CORE_HEADERS (Execute)</title>

<blockquote>
<para>
This event is triggered by the MantisBT bootstrap process just before emitting the
headers. This enables plugins to emit their own headers or use API that enables
tweaking values of headers emitted by core. An example, of headers that can be
tweaked is Content-Security-Policy header which can be tweaked using http_csp_*() APIs.
</para>
</blockquote>
</blockquote>

<blockquote id="dev.eventref.system.coreready">
<title>EVENT_CORE_READY (Execute)</title>

Expand Down
11 changes: 3 additions & 8 deletions plugins/Gravatar/Gravatar.php
Expand Up @@ -106,21 +106,16 @@ function config() {
function hooks() {
return array(
'EVENT_USER_AVATAR' => 'user_get_avatar',
'EVENT_LAYOUT_RESOURCES' => 'csp_headers',
'EVENT_CORE_HEADERS' => 'csp_headers',
);
}

/**
* Add Content-Security-Policy for retrieving Avatar images.
*
* @return void
* Register gravatar url as an img-src for CSP header
*/
function csp_headers() {
# Policy for images: Allow gravatar URL
if( config_get( 'show_avatar' ) !== OFF ) {
# Set CSP header
header( "Content-Security-Policy: img-src 'self' " .
self::getAvatarUrl() );
http_csp_add( 'img-src', self::getAvatarUrl() );
}
}

Expand Down

0 comments on commit b3511d2

Please sign in to comment.