Making of:
The Sanitizer API
Frederik Braun
Staff Security Engineer at Mozilla
@freddyb
1
Questions? Comments?
✉️ freddyb@mozilla.com
1
2
2
[Source: https://wicg.github.io/sanitizer-api/]
3
3
Frederik Braun
@freddyb
Staff Security Engineer
moz://a
4
4
foo.innerHTML = evil
DOM-based XSS
5
As an
Aside
6
How people fix DOM-based XSS
Encode and escape
Sanitize
Use textContent
Replace dangerous stuff with e.g., HTML entities.
If you want to allow some safe subset of HTML, use a Sanitizer
Vulnerable code line doesn’t need HTML?
Use textContent instead.
Done!
7
7
Fixing DOM-based XSS
Encode and escape
Sanitize
Use textContent
Replace dangerous stuff with e.g., HTML entities.
If you want to allow some safe subset of HTML, use a Sanitizer
Vulnerable code line doesn’t need HTML?
Use textContent instead.
Done!
You are here 📌
8
8
What is a Sanitizer?
9
What’s in a Sanitizer?
2. Sanitize
1. Parse
(3. Serialize)
<p>Hello World!
<img src=x
onerror=alert(1)>
<p>Hello World!
<img src="x">
</p>
10
10
Your Sanitizer is mostly an HTML Parser!
11
A Sanitizer API
12
Goals
13
foo.innerHTML = evil
DOM-based XSS
14
First Idea
mySanitizer = new Sanitizer(options)
mySanitizer.sanitize() // String
15
foo.innerHTML =
mySanitizer.sanitize(evil)
16
What’s in a Sanitizer?
2. Sanitize
1. Parse
3. Serialize
<p>Hello World!
<img src=x
onerror=alert(1)>
<p>Hello World!
<img src="x">
</p>
17
17
What’s in "foo.innerHTML=" ?
2. Append
1. Parse
<p>Hello World!
<img src=x
onerror=alert(1)>
18
18
Now we’re using TWO HTML Parsers?!
19
API: Revision 1
mySanitizer = new Sanitizer(options)
mySanitizer.sanitize() // DocFragment
mySanitizer.sanitizeToString() // String
20
foo.append(mySanitizer.sanitize(evil))
21
Nothing good is designed
in a vacuum
Looking for Bugs here. Anyone got some bugs?
22
Sanitizer is less expressive than innerHTML
01
https://github.com/WICG/sanitizer-api/issues/42
Reported by Anne van Kesteren (@annevk)
23
innerHTML
With the Sanitizer
Without the Sanitizer
tableElement.append(
mySanitizer.sanitize(sameInput))
tableElement.innerHTML =
"<tr><td>some cell</td></tr>"
24
24
Parsing HTML fragments
§ 13.4 Parsing HTML fragments
The following steps form the HTML fragment parsing algorithm. The algorithm takes as input an Element node, referred to as the context element, which gives the context for the parser, as well as input, a string to parse, and returns a list of zero or more nodes.
(...)�
(long list of various html elements that cause different parsing behaviors)
25
25
Fragment parsing without context
1. Fragment-parse into <body>
<tr><td>some cell
</tr></td>
26
26
Sanitizer Bypass with iframe srcdoc
02
https://bugzilla.mozilla.org/show_bug.cgi?id=1669945
Reported by Michał Bentkowski (@SecurityMB)
27
Michał’s Bypass: The code
<iframe id=ifr></iframe>
<script>
const bypass =
`<svg><font color><title><u rel="</title><img src onerror=alert(document.domain)>">`;
ifr.srcdoc = new Sanitizer().sanitizeToString(bypass);
</script>
28
28
Parsing within sanitizeToString
<iframe id=ifr></iframe>
<script>
const bypass =
`<svg><font color><title><u rel="</title><img src onerror=alert(document.domain)>">`;
ifr.srcdoc = new Sanitizer().sanitizeToString(bypass);
</script>
└ <svg:svg>
└ <svg:font color="">
└ <svg:title>
└ <html:u rel="</title><img src onerror=alert(1)>">
29
29
String returned from sanitizeToString
└ <svg:svg>
└ <svg:font color="">
└ <svg:title>
└ <html:u rel="</title><img src onerror=alert(1)>">
<svg>
<font color="">
<title>
<u rel="</title>
<img src onerror=alert(document.domain)>"></u></title></font></svg>
30
30
Parsing into the iframe srcdoc
<svg>
<font color="">
<title>
<u rel="</title>
<img src onerror=alert(document.domain)>"></u></title></font></svg>
├ <svg:svg>
└ <html:font color="">
├ <html title>
│ └ #text: <u rel="
├ <html:img src="" onerror="alert(document.domain)"/>
└ #text: ">
31
31
Burn all Parsers!
32
Parsers!!!1
33
33
foo.setHTML(evil, { sanitizer: mySanitizer })
34
foo.setHTML(evil)
35
Security Considerations
Server-Side Reflected and Stored XSS
DOM clobbering
XSS with Script gadgets
Mutated XSS
36
Server-Side XSS
The Sanitizer API is just for DOM-based XSS.
37
DOM clobbering
<form id=f>
<input id=childNodes>foo
<input id=childNodes>bar
<input type=text
value="hidden-from-js">
38
DOM clobbering
Sanitizer API is looking through clobbered properties
Preventing it in your app is currently out of scope.
You could configure the sanitizer to disallow e.g., name & id attributes.
39
XSS with Script gadgets
<button data-html="injection here"
data-html-enabled="true"></button>
[Credit: https://research.google/pubs/pub46450/]
40
XSS with Script gadgets
The Sanitizer can not prevent these attacks.
But you can disallow e.g., data- or role attributes if you customize it according to your framework(s)
41
mXSS
<img src=" test.jpg" alt="``onload=xss()" />
<IMG alt=`` onload=xss() src="test.jpg">
42
mXSS
The Sanitizer offers help against mXSS.
Parse at your own peril.
43
Nothing good is developed without feedback.
We’re still not done here. Gimme moar bugs.
44
Bounties
open Developer Tools
45
Discussion
46
Coding
47
Burn all Parsers!
48
If you need an HTML Parser,
make sure you pick the right one.
49
Thank you!
Frederik Braun (@freddyb)
Staff Security Engineer at Mozilla
Questions? Comments?
✉️ freddyb@mozilla.com
50