Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
/**
* Wacht über serverseitige Werte in HTML-Ereignisattributen.
*
* `json_encode()` allein macht einen Wert nicht attributsicher: Apostroph und
* Anführungszeichen bleiben unverändert und beenden das Attribut, wodurch ein
* eigenes Ereignisattribut eingeschleust werden kann. Sicher ist der Wert erst
* mit HTML-Maskierung (`$this->e(...)` / `htmlspecialchars`) oder mit den
* JSON-Flags, die beide Zeichenarten hexadezimal schreiben.
*/
return static function (): void {
$templateVerzeichnis = dirname(__DIR__) . '/templates';
/** Alle Template-Dateien. */
$dateien = static function (string $verzeichnis): array {
$gefunden = [];
$lauf = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($verzeichnis));
foreach ($lauf as $eintrag) {
if ($eintrag->isFile() && $eintrag->getExtension() === 'php') {
$gefunden[] = $eintrag->getPathname();
}
}
sort($gefunden);
return $gefunden;
};
test('Es werden überhaupt Templates geprüft', function () use ($dateien, $templateVerzeichnis) {
assertTrue(count($dateien($templateVerzeichnis)) > 100, 'Template-Verzeichnis nicht gefunden');
});
test('json_encode in Ereignisattributen ist attributsicher maskiert', function () use ($dateien, $templateVerzeichnis) {
$verstoesse = [];
foreach ($dateien($templateVerzeichnis) as $datei) {
$inhalt = (string)file_get_contents($datei);
// Ereignisattribut (onclick, onsubmit, …) bis zum schließenden Quote,
// sofern darin ein json_encode steht.
if (!preg_match_all(
'/\bon[a-z]+\s*=\s*(["\'])(?:(?!\1).){0,600}?json_encode\s*\((?:(?!\1).){0,600}?\1/is',
$inhalt,
$treffer,
PREG_OFFSET_CAPTURE
)) {
continue;
}
foreach ($treffer[0] as $fund) {
$attribut = $fund[0];
$zeile = substr_count(substr($inhalt, 0, $fund[1]), "\n") + 1;
$maskiert = preg_match('/(htmlspecialchars|->e)\s*\(\s*json_encode/i', $attribut) === 1;
$hexFlags = preg_match('/JSON_HEX_APOS/i', $attribut) === 1
&& preg_match('/JSON_HEX_QUOT/i', $attribut) === 1;
if (!$maskiert && !$hexFlags) {
$verstoesse[] = str_replace($templateVerzeichnis . '/', '', $datei) . ':' . $zeile;
}
}
}
assertSame(
[],
$verstoesse,
"Ungeschütztes json_encode in Ereignisattributen:\n" . implode("\n", $verstoesse)
);
});
};