Skip to content
WANCODE
BM

Finding Security Problems

20 minIntermediateHTML · CSS · JS

In this lesson

  • Explain how inserting user input with innerHTML enables cross-site scripting (XSS), and render it safely
  • Know that everything sent to the browser is public, including "hidden" keys
  • Validate on the server, because client-side checks can be skipped

The big idea

Generated code is optimised to work for the person who asked. Security is about what happens when someone else uses it on purpose in a way you didn't intend.

The guestbook in the editor works perfectly for honest visitors. Now think like a dishonest one. This lesson covers three problems that show up again and again in generated web code. It's a starting point, not a full security course.

1. User input + innerHTML = XSS

innerHTML parses a string as HTML. If that string contains what a visitor typed, the visitor is writing your page's HTML. A message like <img src=x onerror="…"> becomes a real image whose onerror runs their JavaScript in other visitors' browsers — that's cross-site scripting (XSS). It can read what the page shows, act as the logged-in user, or redirect them.

What the assistant wrote

JavaScript
list.innerHTML += `<li><strong>${name}</strong>: ${message}</li>`;

What a reviewer ships

JavaScript
const li = document.createElement("li");
const strong = document.createElement("strong");
strong.textContent = name;            // text, never parsed as HTML
li.append(strong, ": " + message);    // strings become text nodes
list.appendChild(li);

textContent, append("…") and createTextNode treat input as text. React escapes text in JSX the same way — but dangerouslySetInnerHTML brings the problem back.

2. Secrets in frontend code

Every file the browser downloads can be read by anyone: View Source, DevTools, or just fetching your JavaScript bundle. A secret key in frontend code is a published key. It must live on a server (loaded from an environment variable, with .env files kept out of git), and the browser should only receive the result of using it. If a real key has ever been shipped or committed, rotate it.

Some providers issue keys that are designed to be public (often called publishable or client keys) with limited permissions. Check the provider's documentation — and if a key is called "secret", it never belongs in the browser.

3. Trusting client-side validation

maxlength="140" and an if in the submit handler are good for user experience. They are not security: an attacker can edit the page in DevTools, or skip your page entirely and call the API with a tool like curl. The server must check every input as if no form existed.

Asking for a security review

It's worth asking an assistant: "Review this for XSS, exposed secrets, missing server-side validation and authorisation checks. Point to exact lines." Treat the answer as leads to verify, not a clean bill of health — and never paste real secrets while asking.

Post a comment with this message and watch what appears:

<img src=x onerror="document.body.style.background='red'">

Then fix the submit handler in script.js so names and messages are always shown as text: build the <li> with createElement and textContent (or append) instead of innerHTML.

Checks

  • HTML typed by a visitor doesn't become real elements
  • Normal comments still appear

Check understanding

Q1

Which line is safe for showing a username a visitor typed?

Q2

An assistant suggests storing an API secret in a minified JavaScript file "so it's hidden". Is that OK?

Q3

Your form has maxlength="140". Why must the server still check the length?

What you learned

  • Render user input with textContent/createElement, never by building HTML strings for innerHTML.
  • Everything in frontend code is public. Secrets live on the server; leaked keys get rotated.
  • Client-side validation is UX; server-side validation is security.
Preview

Console output from your code appears here.

Step 1 of 3