Prepare Content-Security-Policy for portal (#1138)

This commit is contained in:
Xavier Guimard 2017-01-18 22:36:03 +00:00
parent 1b7e78dbd4
commit 04585cf188
11 changed files with 132 additions and 3 deletions

View File

@ -1,3 +1,4 @@
* Create "csp" in doc
* Test for Zero
* updateStatus( Main, SOAP server )
* replace SOAP calls in Handler/Lib/AuthBasic and Auth/Proxy

View File

@ -33,6 +33,12 @@ sub defaultValues {
'checkXSS' => 1,
'confirmFormMethod' => 'post',
'cookieName' => 'lemonldap',
'cspConnect' => '\'self\'',
'cspDefault' => '\'none\'',
'cspFont' => '\'self\'',
'cspImg' => '\'self\'',
'cspScript' => '\'self\'',
'cspStyle' => '\'self\'',
'dbiAuthnLevel' => 2,
'dbiExportedVars' => {},
'demoExportedVars' => {

View File

@ -714,6 +714,30 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'test' => qr/^[a-zA-Z][a-zA-Z0-9_-]*$/,
'type' => 'text'
},
'cspConnect' => {
'default' => '\'self\'',
'type' => 'text'
},
'cspDefault' => {
'default' => '\'none\'',
'type' => 'text'
},
'cspFont' => {
'default' => '\'self\'',
'type' => 'text'
},
'cspImg' => {
'default' => '\'self\'',
'type' => 'text'
},
'cspScript' => {
'default' => '\'self\'',
'type' => 'text'
},
'cspStyle' => {
'default' => '\'self\'',
'type' => 'text'
},
'customFunctions' => {
'msgFail' => '__badCustomFuncName__',
'test' => qr/^(?:\w+(?:::\w+)*(?:\s+\w+(?:::\w+)*)*)?$/,

View File

@ -449,11 +449,43 @@ sub attributes {
type => 'password',
documentation => 'Secret key',
},
cspDefault => {
type => 'text',
default => "'none'",
documentation => 'Default value for Content-Security-Policy',
},
cspImg => {
type => 'text',
default => "'self'",
documentation => 'Image source for Content-Security-Policy',
},
cspScript => {
type => 'text',
default => "'self'",
documentation => 'Javascript source for Content-Security-Policy',
},
cspStyle => {
type => 'text',
default => "'self'",
documentation => 'Style source for Content-Security-Policy',
},
cspConnect => {
type => 'text',
default => "'self'",
documentation =>
'Authorizated Ajax destination for Content-Security-Policy',
},
cspFont => {
type => 'text',
default => "'self'",
documentation => 'Font source for Content-Security-Policy',
},
portalAntiFrame => {
default => 1,
type => 'bool',
documentation => 'Avoid portal to be displayed inside frames',
},
portalCheckLogins => {
default => 1,
type => 'bool',

View File

@ -605,7 +605,17 @@ sub tree {
'trustedDomains',
'useSafeJail',
'checkXSS',
'lwpSslOpts'
'lwpSslOpts',
{
title => 'contentSecurityPolicy',
help => 'csp.html',
form => 'simpleInputContainer',
nodes => [
'cspDefault', 'cspImg',
'cspScript', 'cspStyle',
'cspConnect', 'cspFont',
]
}
]
},
{

View File

@ -110,6 +110,13 @@
"casStorageOptions": "CAS sessions module options",
"categoryName": "Category name",
"cda": "Multiple domains",
"contentSecurityPolicy": "Content security policy",
"cspDefault": "Default value",
"cspImg": "Image source",
"cspScript": "Script source",
"cspStyle": "Style source",
"cspConnect": "Ajax destinations",
"cspFont": "Font source",
"cfgLog": "Resume",
"checkXSS": "Check XSS attacks",
"clickHereToForce": "Click here to force",

View File

@ -110,6 +110,13 @@
"casStorageOptions": "Options du module des sessions CAS",
"categoryName": "Nom de la catégorie",
"cda": "Domaines multiples",
"contentSecurityPolicy": "Politique de sécurité de contenu",
"cspDefault": "Valeur par défaut",
"cspImg": "Sources des images",
"cspScript": "Sources des scripts",
"cspStyle": "Sources des styles",
"cspConnect": "Destinations des requêtes Ajax",
"cspFont": "Font source",
"cfgLog": "Résumé",
"checkXSS": "Contrôler les attaques XSS",
"clickHereToForce": "Cliquer ici pour forcer",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -68,6 +68,9 @@ has beforeLogout => (
# Custom template parameters
has customParameters => ( is => 'rw', default => sub { {} } );
# Content-Security-Policy header
has csp => ( is => 'rw' );
# INITIALIZATION
sub init {
@ -135,6 +138,14 @@ sub reloadConf {
$self->{conf}->{$key} ||= $conf->{$key};
}
# Initialize content-security-policy header
my $csp = '';
foreach (qw(default img src style font connect)) {
my $prm = $self->conf->{ 'csp' . ucfirst($_) };
$csp .= "$_-src $prm;" if($prm);
}
$self->csp($csp);
# Initialize templateDir
$self->{templateDir} =
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};

View File

@ -632,4 +632,35 @@ sub _dump {
return;
}
sub sendHtml {
my ( $self, $req, $template, %args ) = @_;
my $csp = $self->csp;
push @{ $req->respHeaders },
'X-XSS-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff';
$csp .= "form-action 'self'";
my $url = $args{params}->{URL};
if ( $url and $url =~ s#https?://([^/]+).*#$1# ) {
$csp .= $url;
}
$csp .= ';';
unless ( $self->conf->{portalAntiFrame} == 0 ) {
my @url;
if ( $req->info ) {
@url = map { s#https?://([^/]+).*#$1#; $_ }
( $req->info =~ /<iframe.*?src="(.*?)"/sg );
}
if (@url) {
$csp = join 'frame-ancestors', @url;
}
else {
push @{ $req->respHeaders }, 'X-Frame-Options' => 'DENY';
$csp .= "frame-ancestors 'none';";
}
}
push @{ $req->respHeaders }, 'Content-Security-Policy' => $csp;
return $self->SUPER::sendHtml( $req, $template, %args );
}
1;