Chapter 8 — Programming II
Lesson map
Lesson 08 published. Map, example, EX022.
flowchart TD
N0["Learning outcomes"]
N1["8.1 Untrusted data in pages"]
N2["8.2 Encoding"]
N3["Lab — Lab — escaping output"]
N4["8.3 Lab result"]
N5["8.4 Framework defaults"]
N6["8.5 Context matters"]
N7["8.6 Defence in depth"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX022"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["8.1 Untrusted data in pages"]
N2["8.2 Encoding"]
N3["Lab — Lab — escaping output"]
N4["8.3 Lab result"]
N5["8.4 Framework defaults"]
N6["8.5 Context matters"]
N7["8.6 Defence in depth"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX022"]
[Learning outcomes]
|
v
[8.1 Untrusted data in pages]
|
v
[8.2 Encoding]
|
v
[Lab — Lab — escaping output]
|
v
[8.3 Lab result]
|
v
[8.4 Framework defaults]
|
v
[8.5 Context matters]
|
v
[8.6 Defence in depth]
|
v
[Worked example] --> [Exercise EX022]
Learning outcomes
You will (1) encode output for its context; (2) explain cross-site scripting as a weakness; (3) use framework defaults safely; (4) test output handling.
8.1 Untrusted data in pages
CampusEats shows student reviews of dishes. If review text is inserted into the page as HTML, any markup in it would be interpreted by the browser. This weakness, cross-site scripting (CWE-79), lets one user's text run in another user's browser.
A review containing a script could, for example, act with the reader's session or change what the page shows. Reviews are an obvious target because any logged-in student can write one and many others will read it. Lena therefore treats every review as untrusted text.
8.2 Encoding
Encoding converts special characters to safe forms: < becomes <, & becomes &, " becomes ". The browser then shows them as text.
The key is to encode at the point of output, for the place where the text will appear, rather than cleaning text when it is stored. Stored text then remains exactly what the student wrote, which matters for moderation and for other uses such as email, where different encoding is needed. Lena's lab compares raw and encoded output for a review containing markup characters, so the difference is visible on screen.
Lab — Lab — escaping output
"""Output encoding: user text placed into HTML with and without escaping (synthetic review)."""
import html
review = 'Great pasta <b>5/5</b> & "fast"'
print("raw: <p>" + review + "</p>")
print("escaped: <p>" + html.escape(review) + "</p>")
Run with python3 lab_escape.py. Verified output:
raw: <p>Great pasta <b>5/5</b> & "fast"</p>
escaped: <p>Great pasta <b>5/5</b> & "fast"</p>
8.3 Lab result
The raw version would let the browser treat as bold markup; the escaped version shows "5/5" as literal text, with & and quotes encoded. The review reads the same to people but cannot change the page structure.
Lena adds the lab's sample review to the test suite, so a template change that breaks encoding would fail the build. She also tries a review containing an image tag and a quoted attribute, and both appear as plain text. The comparison makes the abstract rule concrete for new society members, who see exactly how one missing step changes what the browser does.
8.4 Framework defaults
CampusEats' template engine escapes by default. Developers must not turn this off; the few places that need trusted markup use a reviewed helper.
Searching the code for the setting that disables escaping is part of every review, and any use must be justified in the pull request. The reviewed helper accepts only a small set of formatting, such as bold and line breaks, and rejects everything else. Relying on the framework means one well-tested mechanism protects every page, instead of each developer remembering to encode each field.
8.5 Context matters
Text inside an attribute, a script or a URL needs different encoding; frameworks handle common cases, which is another reason to rely on them.
A student's name placed inside a link, for example, needs URL encoding as well as HTML encoding. Lena notes the few places where CampusEats puts user text into scripts or URLs and checks each one by hand, since these are where framework defaults are most likely to fall short. Where possible, the code is changed so that user text appears only in ordinary page content.
8.6 Defence in depth
A Content-Security-Policy header limits which scripts can run, reducing harm if encoding is missed somewhere.
CampusEats' policy allows scripts only from its own domain and the payment provider's, so an injected script would be refused by modern browsers even if an encoding mistake slipped through. The policy is first deployed in report-only mode to find legitimate scripts that would break, and then enforced. It does not replace encoding; it limits the damage when encoding fails. Violation reports are reviewed weekly by the society.
Testing encoding
Tests insert synthetic reviews containing <, >, & and quotes and check the rendered page shows them as text. Each new template that displays user content needs such a test, and the review checklist asks for it.
Worked example
Lena searches the code for the template engine's "unescaped" marker and finds two uses: one for a trusted logo, one for reviews. The review use is a bug from an old feature; it is removed and a test with markup in a review is added.
Synthetic case. Code and reviews are synthetic.
Errors and limits
Escaping is about output context; validating input alone cannot prevent cross-site scripting.
Exercises
- EX022 — XSS meaning. What is cross-site scripting? Rubric: 3 = user text run as code in another's browser; 2 = partial; 1 = vague; 0 = none.
- EX023 — Lab output. How does the escaped output differ? Rubric: 3 = < > & " encoded; shown as text; 2 = partial; 1 = vague; 0 = none.
- EX024 — Code search. What did Lena's search find? Rubric: 3 = two unescaped uses; review one removed; test; 2 = partial; 1 = vague; 0 = none.
Synthesis
Encode output for its context and trust framework defaults.
Continue studying — chapter 08 of 40
← Chapter 07 (draft) · Course chapter index · Chapter 09 (draft) →