Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Désactive la vérification de la Content-Security-Policy en développement #179

Merged
merged 1 commit into from
Mar 1, 2023

Conversation

florimondmanca
Copy link
Collaborator

@florimondmanca florimondmanca commented Feb 28, 2023

Closes #177

Voir #177 (comment)

L'objectif est de faire fonctionner les pages d'erreur (contourner un bug dans Symfony : absence de nonces dans ces pages d'erreur) pour une meilleure expérience de développement.

L'inconvénient est qu'on ne verra pas les éventuels problèmes de CSP avant un déploiement (sur une branche de PR par exemple).

Mais ça résout aussi les problèmes de CSP intempestives liées à Turbo (en partie résolues ici #148) car elles ne se présentaient qu'en local.

@florimondmanca florimondmanca changed the title Fix/csp no dev Désactive la vérification de la Content-Security-Policy en développement Feb 28, 2023
@codecov
Copy link

codecov bot commented Feb 28, 2023

Codecov Report

Merging #179 (97564bb) into main (683e40e) will not change coverage.
The diff coverage is n/a.

@@             Coverage Diff             @@
##                main      #179   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
  Complexity       341       341           
===========================================
  Files             85        85           
  Lines           1436      1436           
===========================================
  Hits            1436      1436           

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@mmarchois
Copy link
Collaborator

Je ne sais pas si cela peut résoudre le problème, mais tu peux override les pages d'erreurs https://symfony.com/doc/current/controller/error_pages.html. Est-ce que tu penses que ça pourrait faire l'affaire ? (je n'ai pas creusé plus que ça).

@florimondmanca
Copy link
Collaborator Author

florimondmanca commented Mar 1, 2023

@mmarchois Je viens d'essayer (j'y ai passé 1h) et je suis à nouveau bloqué par le fait que le HtmlErrorRenderer hardcode le template utilisé et ne permet pas de surcharger renderException() pour utiliser une valeur par défaut de $debugTemplate différente, car cette méthode est privée. cf https://github.com/symfony/symfony/blob/6.3/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php#L125

J'ai d'abord essayé en ajustant directement le HTML produit par le renderer :

<?php

declare(strict_types=1);

namespace App\Infrastructure\Controller;

use Nelmio\SecurityBundle\EventListener\ContentSecurityPolicyListener;
use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;

final class ErrorController
{
    private ErrorRendererInterface $renderer;

    public function __construct(
        KernelInterface $kernel,
        private ContentSecurityPolicyListener $cspListener,
    )
    {
        $this->renderer = new HtmlErrorRenderer($kernel->isDebug());
    }

    public function __invoke(Request $request, \Throwable $exception): Response
    {
        $exception = $this->render($request, $exception);

        return new Response($exception->getAsString(), $exception->getStatusCode(), $exception->getHeaders());
    }

    public function render(Request $request, \Throwable $exception): FlattenException
    {
        $exc = $this->renderer->render($exception);

        $nonce = $this->cspListener->getNonce('script');

        $content = $exc->getAsString();
        $content = \str_replace('<head>', "<head><meta name=\"csp-nonce\" content=\"$nonce\">", $content);
        $content = \str_replace('<script>', "<script nonce=\"$nonce\">", $content);
        $content = \str_replace('<style>', "<style nonce=\"$nonce\">", $content);

        $exc->setAsString($content);

        return $exc;
    }
}

Avec ça j'ai bien les nonces sur les <style /> et dans le header CSP, mais ça ne suffit pas. J'ai toujours l'erreur Content Security Policy: Les paramètres de la page ont empêché le chargement d’une ressource à inline (« style-src »). qui est déclenchée quand Turbo fait documentElement.replaceChild(this.newHead, head);).

Pour les scripts, Turbo retire les nonce="..." pour ensuite les remettre lui-même à partir du <meta name="csp-nonce">, sauf que cette opération est bloquée par la CSP, donc on se retrouve avec des scripts sans nonce. Un hack à l'intérieur du dist de Turbo pour ne pas retirer ces nonces au départ montre que de toute façon ça ne suffit pas à empêcher Turbo de conflicter avec la CSP...

De façon assez rigolote, le ErrorListener de Symfony désactive le header CSP en mode debug : https://github.com/symfony/symfony/blob/ac12c4f9d8d5f3db88201d941ab639d04b08533a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php#L111-L113

En fait, Turbo est chargé dans une page où la CSP est activée (sauf avec cette PR qui désactive la CSP en dev). Donc quand une réponse est obtenue, il va remplacer le head et le body dans un contexte où cette CSP initiale est appliquée par le navigateur. Donc cette précaution de Symfony est inopérante...

Donc ma conclusion était : désactivons la CSP en dev...

@florimondmanca florimondmanca merged commit 5bc3a04 into main Mar 1, 2023
@florimondmanca florimondmanca deleted the fix/csp-no-dev branch March 1, 2023 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

En cas d'erreur lors d'une requête faite par Turbo, la page d'erreur Symfony ne s'affiche pas
2 participants