A guestbook that runs its visitors' notes
A shop adds a guestbook to its site. Visitors leave a note and everyone reads the notes on one page.
One visitor writes a note that is not words but a tiny program. Every later visitor opens the page and their browser quietly runs it. The shop wrote none of it.
The one rule
Warning
This lab is a website that is insecure on purpose. Attack your own private copy and nothing else. It has no way out to the internet. Never try these things on a site that is not yours: in the UK that is a crime under the Computer Misuse Act 1990.
By the end of this lesson
Note
I can stop a page from running text that a visitor typed.
Key words
| Word | What it means | Example |
|---|---|---|
| Cross-site scripting | a page that runs script a visitor typed | XSS |
| Script | a small program that a browser runs inside a page | |
| Escape | swap special characters for safe text | < becomes < |
| Reflected | sent in a request and shown straight back in the reply | |
| Stored | saved by the site and shown to every later visitor | |
| Cookie | a small value the browser keeps and sends back to the site |
The cells students fill in are left blank in this preview.
Typed text can become part of the page
A browser reads a page as text with tags in it. A <b> tag makes bold. A <script> tag runs a program.
If a site puts a visitor's text into the page unchanged, the browser cannot tell typed text from the site's own tags. Script a visitor typed then runs as if the site had written it.
Worked example: reflected and stored
| Reflected | Stored | |
|---|---|---|
| Where the text goes | In the request, then straight back in the reply | Into the site's records, then into later pages |
| Who is hit | The person who opens the crafted link | Everyone who opens the page afterwards |
| Example | A search page that says: you searched for ... | A guestbook or a product review |
| Why stored is worse | One note reaches many visitors with no link to send |
The cells students fill in are left blank in this preview.
Look at the website in your lab
Your lab runs the website you will test. Start it, then run these three. They only look at the sign-in page. They do not sign in and they type no script.
The cookie matters here. A script that runs in a page can read the page's cookies, unless the cookie is marked HttpOnly.
Is the website up?
curl -s -o /dev/null -w "%{http_code}\n" http://dvwa/login.phpShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The cookies the site sets
curl -s -I http://dvwa/login.php | grep -i "^set-cookie"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
How many cookies say HttpOnly?
curl -s -I http://dvwa/login.php | grep -ic httponlyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
What the three answers tell you
The first prints 200. The second prints two lines, one for a cookie called security and one for PHPSESSID, which is the session. The third prints 0.
No cookie says HttpOnly. So script that ran in a page here could read the session cookie. That is why XSS often leads to stolen sessions.
Escaping turns special characters into text
The fix is to escape a visitor's text on its way into a page. < becomes < and > becomes >. The browser draws them as the same characters but never reads them as a tag.
Replace & first. If you replace it last, you turn the & in < into & and the text comes out wrong.
An escape function
def escape(text):
text = text.replace("&", "&")
text = text.replace("<", "<")
text = text.replace(">", ">")
return text
# Change the note and press Run. Then move the & line to the end and run again.
note = "Great bread <b>and</b> cakes & tea"
print("<p>" + escape(note) + "</p>")
A common mistake
Warning
Many people think it is enough to block the word script when a note is saved. It is not. Tags like img carry script too, and filters miss spellings. Escape at the moment you write the text into a page, for every value, and then the question never arises.
Escape on output, and add layers
Escape when you write text into a page, not when you save it. The same text may later go to a page, an email or a file, and each needs its own escaping.
Add layers behind it. Mark session cookies HttpOnly. Send a Content-Security-Policy header that limits which scripts may run. These limit the damage if one escape is missed.