Course / 09 Web security / Lesson 16 of 28

Cross-site scripting

Estimated time: 16 min reading · 12 min activity

A comment should appear as the writer's text. Cross-site scripting, or XSS, occurs when untrusted content can execute code in a page's browser context. The important boundary is where the application turns data into something the browser interprets as instructions or markup.

The Document Object Model, or DOM, is the browser's object representation of a page. A sink is a place where code inserts a value into that representation. Assigning textContent places text into a node; assigning innerHTML asks the browser to parse HTML markup. The harmless bold and italic examples demonstrate that difference without an executable payload.

Before you begin

Estimated time
16 min reading, 12 min activity. Setup and optional practice may take longer.
Where you work
Use the fictional page source, requests and policy models printed here in any current desktop or mobile browser. No server, login, framed account page or socket connection is required.
Without a lab
Read each source fragment or model and predict its result in notes. Identify the boundary, the supporting fact and one limit, then compare with the activity rubric. This does not reproduce a browser exchange. Reading works without JavaScript. Checking requires JavaScript and a secure context; saving additionally requires usable browser storage.

After this lesson you can:

  • Define the point where text becomes markup.
  • Predict the literal and parsed displays for the tea string.
  • Apply the same prediction to the activity string lane.

01Example

Worked string <b>tea</b>. The activity string is <i>lane</i>.

String: the characters <b>tea</b>
Predicted text assignment: those characters, including the brackets.
Predicted markup assignment: the word tea in bold, tags not shown.
The parsed display was not observed.

02Where the string lands

The owned source gets two paragraphs by their id values, literal and parsed. It puts the same string into each through a different property. In <b>tea</b>, <b> opens a bold element and </b> closes it. textContent keeps those characters visible; innerHTML interprets them as tags. The <i>lane</i> activity uses italic tags instead.

Use textContent for untrusted text in this paragraph. If an application intentionally supports HTML, it needs suitable sanitization and controls for that context. Output encoding must fit the specific insertion context; HTML text escaping is not a universal answer for script or URL contexts. A Content Security Policy can reduce some consequences but does not replace safe handling at the sink.

Reflected XSS returns submitted input in the response to the same interaction. Stored XSS saves the input and later serves it to a reader. DOM-based XSS concerns a client-side script handling data unsafely; these descriptions can overlap. Sanitization removes or rewrites unsafe markup according to a policy when HTML is intentionally allowed. Content Security Policy is a browser-enforced set of restrictions on resources and code execution.

Show an untrusted note as characters, and see the two assignments in the owned page do different jobs.Model. A teaching model drawn by hand. It shows the idea, not real tool output.

Read the full explanation below.

samecontext
FieldText assignmentMarkup assignment
Literal input<b>tea</b><b>tea</b>
Assignment contextTextMarkup
Predicted display<b>tea</b>, including bracketstea in bold; tags not shown
ObservationNot observedNot observed
Same characters

The literal input remains the same across contexts.

The literal input remains the same across contexts.

Follow the explanation

  1. The literal input remains the same across contexts.
  2. Text preserves the brackets as characters; markup predicts formatting. No parsed display was observed.
Read every state and value

1. The literal input remains the same across contexts.

Selected Comparison
same
Highlighted Field
Literal input

2. Text preserves the brackets as characters; markup predicts formatting. No parsed display was observed.

Selected Comparison
context
Highlighted Field
Assignment context · Predicted display
The insertion sink changes interpretation; this harmless prediction is not an observed browser exploit.

03Predict the lane string

Read the evidence before comparing with the activity rubric. Do not open the file in a browser for this checkpoint.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Desk note</title>
</head>
<body>
  <p id="literal"></p>
  <p id="parsed"></p>
  <script>
    var note = "<b>tea</b>";
    document.getElementById("literal").textContent = note;
    document.getElementById("parsed").innerHTML = note;
  </script>
</body>
</html>

Download the desk page

Predicted display.

The string in the script is the ten characters < b > t e a < / b > with the letters of tea between the tags.
textContent assignment: the literal paragraph shows those characters, including the angle brackets.
innerHTML assignment: a browser that parses HTML would show the word tea in bold and would not show the tags.
That parsed display was not observed.

Activity string, different from the script: <i>lane</i>
Predicted literal display: the characters <i>lane</i>
Predicted parsed display: the word lane in italic.

Download the predicted display

  1. Read the activity string on the text card. Write the characters.
  2. Write the predicted literal display and the predicted parsed display.
  3. Check: the literal display is the characters <i>lane</i>. The parsed display would be the word lane in italic, and it was not observed. The tea string is the worked example.
  4. Stop. Do not paste a third-party script into the page.

04Optional challenge

From the two words reflected and stored, say which one fits a note that was saved and later shown, and which fits a note that comes back only in this response. The rubric is those two placements. About ten minutes.

05Fresh cases for the checks

Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.

DOM case A: fresh harmless string modelOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

String value: <b>cedar</b>
P: paragraph.textContent receives that string.
Q: paragraph.innerHTML receives that string.
Rule: textContent keeps characters as text; innerHTML parses HTML markup.
No browser rendering or script execution was observed.

DOM case B: independent caller textOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

The product requirement is to display every caller character literally in a paragraph.
The handler currently assigns caller text to paragraph.innerHTML.
The application does not need user-authored HTML formatting.

06Explain your evidence

Optional written self-check: explain one decision using the supplied case. No tool run is required.

Check your explanation: predict both receiving properties on the same harmless string; locate the insertion boundary; choose the sink for literal text; distinguish predictions from observed rendering and avoid claiming arbitrary script execution.

07Common mistakes

If the lane check shows a bold tea, you used the worked string.

If you treat the italic prediction as observed, the card says no browser was opened.

If you rely on a policy header and still assign the string as markup, the insertion point is unchanged.

References