Ou comment supprimer unsafe-inline et unsafe-eval de script-src dans l'en-tête HTTP Content-Security-Policy avec Wordpress et obtenir un A+ sur securityheaders.io.
Déjà, autant prévenir, ça va être long et pénible... Cela se fera à coup de petite modification, test, re petite modification. Oui modification est au singulier, car on ne change qu'un paramètre à la fois entre chaque test.
Wordpress seul est dans l'ensemble assez propre, dans le sens où il n'embarque que très peu de javascript inline. Il n'y a donc que quatre problèmes à résoudre
Ça parait simple dit comme ça, mais dans la pratique, le moindre plugin (surtout ceux de galerie d'images) peut venir violer la politique de sécurité du contenu (Content-Security-Policy). De plus, pour avoir les rapports de violation des CSP, il va falloir un script qui enregistre tout ça.
Fichier /usr/share/wordpress/report.php
<?php
date_default_timezone_set('UTC');
$LOGPATH = "/var/log/httpd";
$ROWS = array(
'violated-directive',
'effective-directive',
'blocked-uri',
'document-uri',
'line-number',
'status-code',
'referrer',
'disposition',
'original-policy',
'script-sample'
);
$header = date("Y-m-d H:i:s");
$raw = file_get_contents('php://input');
if ( ! isset( $raw ) or empty ( $raw ) or strlen( $raw ) >= 2048 ) {
exit( 1 );
}
$rows = json_decode( $raw );
$message = $header . ' csp-report' . PHP_EOL;
foreach( $ROWS as $row ) {
$message .= " $row: " . $rows->{'csp-report'}->{$row} . PHP_EOL;
}
$message .= PHP_EOL;
$file = file_put_contents( $LOGPATH . "/report.log", $message, FILE_APPEND | LOCK_EX );
echo "For reporting Content-Security-Policy violation";
exit( 0 );
On va commencer en mettant une définition très restrictive dans la configuration apache.
Header always set Content-Security-Policy "default-src 'self' data: ; script-src 'self' ; style-src 'self' 'unsafe-inline' ; font-src 'self' data: ; img-src 'self' data: ; report-uri https://blog.example.com/report.php"
On désactive tous les plugins Wordpress
On verra apparaitre au rechargement de la page, les premiers rapports, dans le fichier /var/log/httpd/report.log
C'est le seul problème du cœur de Wordpress (hors partie admin) concernant la politique de sécurité du contenu: les emojis.
2017-11-12 12:02:29 csp-report
violated-directive: script-src https://www.tartarefr.eu
effective-directive:
blocked-uri: self
document-uri: https://www.tartarefr.eu/
line-number: 12
status-code:
referrer:
disposition:
original-policy: default-src https://www.tartarefr.eu data:; script-src https://www.tartarefr.eu; style-src https://www.tartarefr.eu 'unsafe-inline'; font-src https://www.tartarefr.eu data:; img-src https://www.tartarefr.eu data:; report-uri https://report.tartarefr.eu/report.php?type=csp
script-sample:
window._wpemojiSettings = {"baseUrl"...
Dans le CSS de notre thème, il y a une dépendance à https://fonts.googleapis.com
2017-11-12 12:02:29 csp-report
violated-directive: style-src https://www.tartarefr.eu 'unsafe-inline'
effective-directive:
blocked-uri: https://fonts.googleapis.com
document-uri: https://www.tartarefr.eu/
line-number:
status-code:
referrer:
disposition:
original-policy: default-src https://www.tartarefr.eu data:; script-src https://www.tartarefr.eu; style-src https://www.tartarefr.eu 'unsafe-inline'; font-src https://www.tartarefr.eu data:; img-src https://www.tartarefr.eu data:; report-uri https://report.tartarefr.eu/report.php?type=csp
script-sample:
On ajoute https://fonts.googleapis.com à notre en-tête Content-Security-Policy, dans la section style-src et on recharge Apache.
Header always set Content-Security-Policy "default-src 'self' data: ; script-src 'self' ; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com ; font-src 'self' data: ; img-src 'self' data: ; report-uri https://report.example.com/report.php?type=csp"
2017-11-12 12:06:52 csp-report
violated-directive: font-src https://www.tartarefr.eu data:
effective-directive:
blocked-uri: https://fonts.gstatic.com
document-uri: https://www.tartarefr.eu/
line-number:
status-code:
referrer:
disposition:
original-policy: default-src https://www.tartarefr.eu data:; script-src https://www.tartarefr.eu; style-src https://www.tartarefr.eu 'unsafe-inline' https://fonts.googleapis.com; font-src https://www.tartarefr.eu data:; img-src https://www.tartarefr.eu data:; report-uri https://report.tartarefr.eu/report.php?type=csp
script-sample:
Header always set Content-Security-Policy "default-src 'self' data: ; script-src 'self' ; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com ; font-src 'self' data: https://fonts.gstatic.com ; img-src 'self' data: ; report-uri https://report.example.com/report.php?type=csp"
À partir d'ici, on reçoit plus de rapport de violation, sauf si on essaie la partie administrateur ou un plugin de galerie.
La partie administrateur du site ne fonctionne pas bien avec notre définition de Content-Security-Policy. On va encore l'adapter mais à la fin il faudra tricher pour la section script-src et modifier l'en-tête HTTP Content-Security-Policy pour la partie admin. On va donc ajouter à notre définition Content-Security-Policy
Pour le reste, pas de recette miracle, hélas. La définition dans Apache devient ceci:
<Location "/wp-admin">
...
Header always set Content-Security-Policy "default-src 'self' data: ; script-src 'self' 'unsafe-inline' 'unsafe-eval' ; style-src 'self' 'unsafe-inline' fonts.googleapis.com ; font-src 'self' fonts.gstatic.com data: ; img-src 'self' data: secure.gravatar.com ; report-uri https://report.example.com/report.php"
</Location>
Header always set Content-Security-Policy "default-src 'self' data: ; script-src 'self' ; style-src 'self' 'unsafe-inline' fonts.googleapis.com https://code.jquery.com ; font-src 'self' fonts.gstatic.com data: ; img-src 'self' data: https://code.jquery.com https://secure.gravatar.com ; report-uri https://report.example.com/report.php"
Bien évidemment, on ne teste pas la partie admin ...