48
XSS and How to Escape Tyler Peterson @managerJS

XSS and How to Escape

Embed Size (px)

Citation preview

Page 1: XSS and How to Escape

XSS and How to EscapeTyler Peterson@managerJS

Page 2: XSS and How to Escape

Bottom Line Up-Front (BLUF)• Anything from the user is

definitely unsafe.• Don’t render unsafe data

into script tags.• Do render html-escaped

data into html tags (including meta-tags).

Page 3: XSS and How to Escape

Strange Bedfellows• XSS is a common attack vector

• Escaping is a commonly used countermeasure

• Can be effective but not a perfect fit

Page 4: XSS and How to Escape

Escaping is All About Magic• Every interesting computing

context is a mix of data and magic.• The magic is triggered by special

data sequences.• It’s just like a wizard that can carry

out a normal conversation with no magic, then invoke magic using key-words.

Page 5: XSS and How to Escape

Escaping (itself)• Escaping prepares data to enter a context with

different magic rules.

Escaping Algorithm: Deathstar to HogwartsReplace all protocol droids with house elves.

Page 6: XSS and How to Escape

Magic Mismatch• When you don’t properly prepare data for the new

context you end up triggering magic on accident.

Page 7: XSS and How to Escape

(Or On Purpose…)• Nefarious folks capitalize on our escaping mistakes

and abuse magic we fail to protect.

Page 8: XSS and How to Escape

But Back to Escaping and Magic

Page 9: XSS and How to Escape

Real Magic• RegExp: punctuation is magic, others are not

(mostly)

• HTML: <, >, and & are magic

• URL: /, &, ?, and # are magic

• SQL: ' and ` are magic

Page 10: XSS and How to Escape

Really Real Magic• RegExp: different things are magic inside a character

class. E.g. - as in [-a-z]• HTML: Different things are magic inside a tag

definition. e.g. =, ", '. • URL: Different things are magic in host, path, query,

and fragment. E.G. the first ? begins the query, but they aren’t special in the fragment.

• SQL: Different things are magic inside a string: '' is an escaped quote.

Page 11: XSS and How to Escape

Even Simple Languages are Hard• RegExp, HTML, URL, and SQL have magic that can

be difficult to reason about.

• Programming languages, like JavaScript, are even more complicated.–More contexts with different nuances–More magic and less data

Page 12: XSS and How to Escape

Enter Cross Site Scripting (XSS)others making your page misbehave

Page 13: XSS and How to Escape

Simple Data Rendering Example// template.ejsvar lang = "<%- locale %>";

• NOTE: I’m using EJS in these examples but the problems I illustrate are fundamental.

Page 14: XSS and How to Escape

Simple XSS Attack• Attacker sendslocale = 'en"; doEvil(); "throw away string literal?'

• You rendervar lang = "en"; doEvil(); "throw away string literal";

Page 15: XSS and How to Escape

Simple Escaping Countermeasure// template.ejsvar lang = "<%= locale %>";

You changed this to EJS’s back fat arrow. This does HTML escaping of the string before rendering it.

Page 16: XSS and How to Escape

Attack Foiled!• This XSS vulnerability is closed.• The doEvil() function or code is NOT invoked.

Page 17: XSS and How to Escape

How Did it Work?$ node> var ejs = require('ejs')undefined> var locale = 'en"; doEvil(); "throw away string literal'undefined> ejs.render('var lang = "<%- locale %>";')'var lang = "en"; doEvil(); "throw away string literal”;'> ejs.render('var lang = "<%= locale %>";')'var lang = "en&#34;; doEvil(); &#34;throw away string literal";'

1

2

Page 18: XSS and How to Escape

How Good is the Fix?• No hands necessary, but please reflect:

– Have you ever done this?

– How certain are you that this is a high-quality fix?

Page 19: XSS and How to Escape

Sidebar Example• You are hosting a 1 day, 2 event, classic video

game tournament.– Contra– Classic Doom

• No cheat codes allowed– Cheat codes are like magic– You can escape the codes to render them inert

Page 20: XSS and How to Escape

Escape the Konami Code• What are you going to look for?

↑↑↓↓←→←→BA • You foil this cheat by inserting two “start”

commands right before the A.

Page 21: XSS and How to Escape

Escape the Doom Clipping Code• What are you going to look for?– idspispopd

• You foil this by inserting any letter (but d) before the final d.

Page 22: XSS and How to Escape

Flint’s Dad Mixes It Up• Your less adept colleague mixes up the cheat

detectors.• They work great but can’t stop the cheating.

Page 23: XSS and How to Escape

Same Thing Happens With Escaping• Sometimes we escape till it works, but it’s really

not right.

Page 24: XSS and How to Escape

Escaping Has Sharp Edges• Most escape algorithms treat most data the same,

because most data is non-magic most of the time.• The characters they treat differently—the edge

cases—are the most important parts to consider.• Take away: – It’s not enough to casually test an application of

escaping. – You need to thoroughly understand the old context, the

new context, and the joining algorithm.

Page 25: XSS and How to Escape

Back to XSS: <%= Worked, but…• What if you had a number instead of a string?

> var onServer = '6; doEvil();'undefined> ejs.render('var count = <%= onServer %>')'var count = 6; doEvil();'

Page 26: XSS and How to Escape

Missed Some Magic• The fix worked at first because " is magical in

JavaScript and HTML

• The fix failed because ; is only magical in JavaScript

Page 27: XSS and How to Escape

Good Enough?• So, you’re kinda safe as long as you are using

strings OR at least match the untrusted string with a RegEx like /[^;'"]*/ and use the matched text instead of the full text.

Page 28: XSS and How to Escape

My Tools Have Betrayed Me?!• Why is EJS so broken? Why doesn’t escaping help

me escape?

• It isn’t broken.

• Escaping isn’t a security measure. It only ferry’s data between magical worlds.

Page 29: XSS and How to Escape

Escaping Must Match ContextFor escaping to be reliable you

have to match the new data context with the escaping

algorithm.

• The problem is that <%= (back fat arrow) is an HTML escape and you are rendering text into a JavaScript execution context.

Page 30: XSS and How to Escape

The (Nonexistent) JavaScript Escape• So just switch to using JavaScript escape. Well,

there isn’t a standard JavaScript escape function so you can’t.

• What’s more, JavaScript has so many contexts that you shouldn’t write one.

Page 31: XSS and How to Escape

What’s the Right Way™?• Render into HTML with an HTML escape

// template.ejs<meta name="lang" content="<%= lang %>">

• HTML escape replaces ', ", and >. An attacker can’t end the attribute.

Page 32: XSS and How to Escape

The Right Way™ to Read:var metas = document.getElementsByTagName('meta');var i, l = metas.length, lang; for (i=0; i < l; ++i) { if (metas[i].getAttribute('name') == 'lang') { lang = metas[i].getAttribute('content'); }}

Page 33: XSS and How to Escape

The Right Way™ is Kinda Yucky

• No wonder we take short-cuts.• Really is a good way to match escaping algorithms.• Read is awkward from scratch.

Page 34: XSS and How to Escape

I Lied About JavaScript• The standard, safe way to encode data in

JavaScript is JSON.stringify().• JSON is a form of escaping so you must be careful

not to double escape.• Forces all data into a string context.

• npm/js-string-escape is similar and popular

Page 35: XSS and How to Escape

JSON Example

> ejs.render('var count = <%- JSON.stringify(number) %>')'var count = "6; doEvil()"'

• JSON does the escaping so EJS doesn’t have to (in fact MUST NOT).

• Notice that JSON added quotes.

1

Page 36: XSS and How to Escape

Show of Hands• Who likes the Right Way™?

• Who’s going to use the JSON Way?

Page 37: XSS and How to Escape

OK, I Lied About JSON Being Safe• You’re not rendering into a JavaScript context.

• You’re rendering into a JavaScript context through an HTML context.

• Both magics can apply!

Page 38: XSS and How to Escape

Here’s Your Template<!DOCTYPE html><html> <head><title>JSON Demo</title></head> <body> <script type="text/javascript"> var locale = <%- JSON.stringify(locale) %>; </script> <h1>Was it Safe?</h1></body></html>

Page 39: XSS and How to Escape

These Attacks Work• '</script><h1>embarrassing content</h1>'• '</script><script>doEvil();</script><script>'

Page 40: XSS and How to Escape

Rendering into a Script Tag is Doomed• Adding <%= makes the happy path fail• < and " are magical to HTML– so you have to escape them.– But the browser doesn’t replace the entity reference so

JavaScript sees an & and chokes.• Even if you found a way to do it–Would you remember it?–Would your team-mates understand and perpetuate it?

Page 41: XSS and How to Escape

You Can’t Mix the Magics

Page 42: XSS and How to Escape

Rules For Us Mere Mortals• The more you have to reason about the security of

a fix the less secure it is.• Every step in logic is an opportunity for error and

exploit.• In general, straightforward and yucky is more

secure than well reasoned and slick.• You can be slick, but you’re taking on risk.

Page 43: XSS and How to Escape

Take Away: XSS Abuses Magic• A cross-site scripting attack is normally magic

masquerading as data.

Page 44: XSS and How to Escape

Related: De-taint• Block the bad data at the front door.• No general solution.• Ideally unnecessary.• Escaping errors abound, so still a good idea to use.

Page 45: XSS and How to Escape

Example: De-taint localevar pat = /[a-zA-Z_]{2,5}/pat.exec('en_US"; evil()')[0] // "en_US"

• Effectively limits evil.• Can accidentally be too restrictive, so be liberal.• Evil looking inputs are sometimes valid, so this

can’t be your only solution.

Page 46: XSS and How to Escape

Final Recommendation• HTML escape data into meta tags and retrieve

them from JavaScript.• Pick a safe way and stick to it. No shortcuts.

Page 47: XSS and How to Escape

Final Questions?

Page 48: XSS and How to Escape

Contact MeTyler Peterson

Web Development [email protected]

@managerjs

48