Aller au contenu

Tous les billets

Le nonce et la page figée

Une politique de sécurité stricte, un nonce généré à chaque requête, et une page qui se chargeait sans le moindre JavaScript.

cspnextsécurité

Symptôme : la page s’affiche, les styles sont là, et rien ne répond. La console est un mur de refus.

Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'nonce-...' 'strict-dynamic'"

Le nonce est pourtant bien dans l’en-tête. Il change à chaque rechargement. Il est correctement formé.

Le malentendu

Un nonce ne vaut que s’il est unique par réponse. C’est toute sa sécurité : un attaquant qui injecte un script ne peut pas deviner la valeur du jour, puisqu’il n’y a pas de valeur du jour.

Ma politique était posée dans un middleware, qui s’exécute à chaque requête. Le HTML, lui, avait été produit une fois pour toutes à la compilation. Il portait donc un nonce figé — celui du build, ou aucun — pendant que l’en-tête en annonçait un nouveau à chaque visite.

Les deux ne coïncidaient jamais. Le navigateur faisait exactement son travail : il refusait tous les scripts, y compris ceux du cadre applicatif lui-même.

La correction

Une ligne, dans la mise en page racine :

export const dynamic = 'force-dynamic';

La page est désormais rendue à la demande, et ses balises script reçoivent le nonce de la requête en cours.

Ce qui m’a servi de leçon

J’avais d’abord « corrigé » le problème en recopiant la politique sur les en-têtes de la requête, en plus de ceux de la réponse. Le site s’est remis à fonctionner. J’ai attribué la réparation à cette ligne.

Elle n’y était pour rien. Le rendu dynamique, ajouté dans le même mouvement, faisait tout le travail. Je l’ai su en retirant chaque changement l’un après l’autre pour voir lequel faisait échouer les tests : sans force-dynamic, huit tests sur neuf tombent. Sans la ligne du middleware, aucun.

Une correction qui coïncide avec la disparition d’un symptôme n’est pas une correction démontrée. Le seul moyen de le savoir est de la retirer et de vérifier que le symptôme revient.

Et le coût ?

Réel, mais nul à cette échelle : aucune de ces pages n’a de contenu coûteux à produire. L’alternative aurait été d’autoriser 'unsafe-inline', ce qui aurait retiré à la politique l’essentiel de sa valeur — sur une origine qui détient des données chiffrées, c’est hors de question.