Prepare Content-Security-Policy for portal (#1138)
This commit is contained in:
parent
1b7e78dbd4
commit
04585cf188
|
@ -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
|
||||
|
|
|
@ -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' => {
|
||||
|
|
|
@ -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+)*)*)?$/,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user