Basic

The page runs your input

I put a user's text straight into the HTML and a script tag in the text executed. Escaping turns the text into data the page cannot run.

What you will see

renders as text, no alert fires

About 1 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Escape user text and confirm the tag no longer runs.

1

Escape a string that contains a script tag. The angle brackets become entities.

import html print(html.escape(''))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
<script>alert(1)</script>
2

The escaped text renders as visible text, the browser does not run it.

print('renders as text, no alert fires')

Before you run it, say out loud what it will print. Then open the answer.

What it prints
renders as text, no alert fires
Escaping < alone is not enough inside an attribute value, a quote can break out. Escape quotes too, or set the attribute through an API that quotes for you.

Data Flow

User text is escaped so markup characters become inert data; an auto-escaping template does this by default and the raw form is the hole.
1 User text arrives
↓
2 Escape turns < into <
↓
3 Browser shows text not code
↓
4 Raw form skips escaping
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

import html sets nothing on screen
print(html.escape('<script>alert(1)</script>')) &lt;script&gt;alert(1)&lt;/script&gt;
Why this works
Data is not code

The rule is one sentence: user text must arrive as data, never as markup. Escaping is how you enforce it. Any place you splice raw text into HTML, or set innerHTML from user input, is an XSS hole.

Attributes too

Escaping < is not enough inside an attribute, a value can break out with a quote. Escape quotes as well, or set the attribute through an API that quotes for you. Attribute injection is XSS by another door.

Examples

The injection

<input>  <- user typed this
<script>alert(1)</script>

Raw text becomes markup. A <script> in the text is now real code the browser runs. That is XSS.

Escape the text

html.escape(user_text)  # < becomes &lt;

Escaping turns < into &lt;, so the browser shows the tag as text, not code. The data stays data.

Templates do it for you

<%= user_text %>

An auto-escaping template (EJS <%=, most frameworks) escapes by default. The bug is using the raw form (<%- or innerHTML) that skips it.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Keeping the key secret
Previous Lesson
Keeping the key secret
Next Lesson
A form from another site
A form from another site →