Tous les articles

Comment lire un rapport de violation CSP

CentralCSP Team ·

Dernière mise à jour:

Vous avez activé le reporting de votre politique de sécurité du contenu (CSP), les rapports arrivent, et vous voilà devant du JSON à essayer de comprendre ce qui a bloqué quoi. Les noms de champs sont laconiques, certaines valeurs sont masquées, et le même événement peut arriver sous deux formes différentes selon la façon dont vous avez câblé le reporting. Voici un guide champ par champ d'un rapport de violation CSP pour que vous puissiez en lire un d'un coup d'œil et savoir quelle ligne de votre politique modifier.

Il existe deux formats pour le même événement sous-jacent. Le rapport csp-violation moderne est livré via la Reporting API dans le cadre d'un lot ; le format report-uri historique est un objet unique que le navigateur envoie en POST de son côté. Ils transportent la même information sous des noms de champs différents, donc une fois que vous savez lire l'un, vous savez lire l'autre.

La forme moderne, csp-violation

Voici ce que vous obtenez quand la politique utilise la directive report-to. Le navigateur livre un tableau avec Content-Type: application/reports+json, et les données spécifiques à la CSP se trouvent dans body avec des noms de champs en camelCase.

{
  "type": "csp-violation",
  "age": 53,
  "url": "https://api-next.centralcsp.com/",
  "user_agent": "Mozilla/5.0 ...",
  "body": {
    "documentURL": "https://api-next.centralcsp.com/",
    "referrer": "https://www.google.com/",
    "blockedURL": "https://apis.google.com/js/platform.js",
    "effectiveDirective": "script-src-elem",
    "originalPolicy": "default-src 'self'; report-to csp-endpoint",
    "sourceFile": "https://api-next.centralcsp.com/",
    "sample": "",
    "disposition": "enforce",
    "statusCode": 200,
    "lineNumber": 1441,
    "columnNumber": 59
  }
}
ChampCe qu'il signifie
documentURLLa page où la violation s'est produite.
blockedURLLa ressource qui a été bloquée. En cross-origin, elle est tronquée au schéma, à l'hôte et au port.
effectiveDirectiveLa directive qui s'est réellement déclenchée (par exemple script-src-elem).
originalPolicyLe texte complet de la politique appliquée par le navigateur.
dispositionenforce pour une politique bloquante, report pour Report-Only.
sampleLes ~40 premiers caractères du contenu inline incriminé, uniquement quand la directive porte 'report-sample'.
statusCodeLe statut HTTP de la réponse du document.
lineNumber / columnNumberL'emplacement pour les violations inline.

Deux de ces champs surprennent. blockedURL est délibérément masqué jusqu'à l'origine pour les ressources cross-origin, donc vous voyez https://cdn.example/ plutôt que le chemin exact ; c'est une mesure de confidentialité, pas un bug. Et effectiveDirective est la directive précise qui s'est déclenchée, qui peut être plus fine que ce que vous avez écrit : une balise <script> bloquée rapporte script-src-elem même si votre politique ne définissait que script-src, parce que script-src est le repli dont la directive plus fine hérite.

Si vous analysez ces données sur votre endpoint, voici le body moderne sous forme de type TypeScript que vous pouvez déposer dans le handler. Survolez n'importe quel champ pour voir son type ; disposition est une union des deux seules valeurs qu'il peut prendre.

// The body of a modern csp-violation report.
interface CspViolationBody {
  : string;
  : string;
  : string;
  : string;
  : string;
  : string;
  : string;
  : "enforce" | "report";
  : number;
  : number;
  : number;
}

interface CspViolationReport {
  : "csp-violation";
  : number;
  : string;
  : string;
  : CspViolationBody;
}

function (: CspViolationReport) {
  const  = ..;
  const  = .. === "enforce";
  return { ,  };
}

La forme historique, report-uri

Quand la politique utilise la directive report-uri, le navigateur envoie en POST un objet unique avec Content-Type: application/csp-report et des noms de champs en kebab-case.

{
  "csp-report": {
    "document-uri": "https://api-next.centralcsp.com/",
    "blocked-uri": "https://apis.google.com/js/platform.js",
    "effective-directive": "script-src-elem",
    "violated-directive": "script-src-elem",
    "original-policy": "default-src 'self'; report-uri /csp-reports",
    "disposition": "enforce",
    "status-code": 200,
    "script-sample": ""
  }
}

La correspondance est de un à un : blockedURL correspond à blocked-uri, effectiveDirective à effective-directive, sample à script-sample, et ainsi de suite. Le seul champ supplémentaire, violated-directive, est un alias historique de effective-directive et peut être ignoré. Si votre endpoint accepte les deux formats, faites une branche selon l'en-tête Content-Type. Quelle directive vous devriez envoyer, et pourquoi vous envoyez souvent les deux, est traité dans report-uri vs report-to.

Lire un rapport en pratique

Lisez effectiveDirective et blockedURL ensemble ; ce couple vous dit presque tout. Une violation script-src pointant vers un hôte que vous reconnaissez (un CDN, un fournisseur d'analytics) signifie généralement que votre politique est trop stricte et que vous devez autoriser cette source. Une violation pointant vers un hôte que vous ne reconnaissez pas, ou un sample inline que vous n'avez pas écrit, mérite une enquête comme injection possible. Pour remonter d'une seule violation jusqu'à la ligne qui l'a causée, déboguez les violations CSP dans les DevTools de Chrome.

Le champ sample est le plus utile pour les violations inline, mais il n'apparaît que lorsque la directive porte le mot-clé 'report-sample', et il est plafonné à environ 40 caractères, assez pour identifier l'extrait, pas pour fuiter un secret. disposition vous dit s'il s'agissait d'un vrai blocage (enforce) ou d'un essai à blanc (report), ce qui compte quand vous exécutez une politique appliquée et une politique Report-Only en même temps.

Traitez chaque champ comme une entrée influencée par un attaquant. Un rapport est une donnée que la page a poussé le navigateur à envoyer, donc n'affichez jamais blockedURL ou sample sur une page d'administration sans échappement.

Arrêtez de lire du JSON brut

Lire un rapport, c'est bien ; en lire des milliers, non. CentralCSP ingère les deux formes, les normalise dans un seul modèle, et regroupe les violations par directive et par hôte bloqué, donc au lieu d'un fichier de log vous obtenez une liste classée de ce qui casse vraiment et à quelle fréquence. Elle les affiche sur un dashboard sur lequel vous pouvez agir, ce qui fait la différence entre collecter des rapports et les utiliser.

Étapes suivantes

Collectez et regroupez automatiquement les rapports CSP.

Sources