Capstone project

Capstone: secure a comment section

Lesson 20 of 20 · 30 min · Module 6: Defence in depth and the capstone

Time for a security review of your own. This comment section works, but it contains the holes you have learned to spot. Fix every one of them without taking away the features people use: formatting, links to their websites, and posting.

Start this lesson →

Free. No installation: you write and run the code in your browser, and it is checked automatically.

By the end, you’ll be able to…

  • Review code the way an attacker would
  • Combine the defences of the whole course in one feature
  • Keep a secure feature friendly to use

The idea

Review the code like an attacker would: where does untrusted data enter the page, which requests change data, and what can a request carry that it should not? Then fix each problem with the tools you know: sanitize() for formatted text, textContent for plain text, safeUrl() and safe link attributes for websites, a CSRF token for posting, server-side identity, input checks, and polite handling of 429.

const response = await fetch('/api/comments', { method: 'POST' /* … */ });
if (response.status === 429) {
  const seconds = Number(response.headers.get('Retry-After'));
}

01Follow the data

List every place where outside data enters: the comments from the server, each comment’s website, the text people type, and the server’s answers. Each one needs the right treatment: sanitize, show as text, or check the address.

02Follow the requests

For every request that changes data, ask three questions: does it prove it came from your page (the CSRF token), does it carry only what the server cannot know by itself (no author), and is anything secret in the address (nothing should be)?

headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken },
body: JSON.stringify({ text })

03Security people barely notice

People can still format text, link their website and post quickly. Good security removes the dangerous parts and explains limits clearly, such as how long to wait after posting too fast, instead of failing silently.

Your project brief

Secure the comment section:
• renderComment: show the comment’s text through sanitize() in .text; show the author as text in .author, which is a link only when safeUrl(website) accepts the address, with target="_blank" and rel containing noopener and noreferrer, and a <span class="author"> otherwise.
• Posting: trim the text and refuse less than 1 or more than 500 characters with Write between 1 and 500 characters. Send only { text } as JSON to /api/comments, with no query string and with the X-CSRF-Token header from the page; the server knows who is signed in. Disable #post while sending.
• Answers: on success, add the returned comment at the top, clear the form and show Comment posted.; on 429, show You are posting too fast. Try again in 1 second. (with the number from Retry-After, and seconds for other numbers) and keep the button disabled for that long; on other errors show the answer’s error as text; if the request fails, show Could not reach the server. Please try again.

What the checks look for

  • Comments load and appear
  • Comment text keeps only safe formatting
  • Author names are plain text
  • Only safe websites become links, and they open safely
  • New comments carry the CSRF token
  • The author comes from the session, not the page
  • Empty and overlong comments are refused
  • Posted comments appear safely at the top
  • Server errors are shown as text
  • Posting too fast waits as long as the server asks
  • A lost connection is explained
Try it in the editor →

Take this with you

Untrusted data is shown safely, requests prove where they come from, and limits are explained.

Go a little further

Add an Edit button to your own comments that sends a PATCH request with the CSRF token, and show a countdown while posting is paused.

Learn Web Security Foundations for free

20 short lessons with real code and instant checks.

See the whole course →