<script src=”https://ajax.googl
eapis.com/ajax/libs/jquery/1.8.
0/jquery.min.js” integrity=”typ
e:text/javascript sha512-AODL7i
dgffQeNsYdTzut09nz9AINcjhj4jHD7
2HcLirsidbC8tz+dof7gceOCQD8Wske
uRFfJ9CsgZTHlMiOYg==”></script>
Integrity protection for
3rd
-party JavaScript
François Marier @fmarier mozilla
Firefox
Security & Privacy
Web Platform
Web Platform
Content Security Policy
aka CSP
Content Security Policy
aka CSP
mechanism for preventing XSS
telling the browser what external
content is allowed to load
what does CSP look like?
$ curl --head https://mega.nz
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1989
Content-Security-Policy:
default-src 'self' *.mega.co.nz *.mega.nz
http://*.mega.co.nz http://*.mega.nz;
script-src 'self' mega.co.nz mega.nz data:
blob:;
style-src 'self' 'unsafe-inline' *.mega.nz
*.mega.co.nz data: blob:;
frame-src 'self' mega:;
img-src 'self' *.mega.co.nz *.mega.nz data:
Hi you<script>
alert('p0wned');
</script>!
Tweet!
What's on your mind?
without CSP
Hi you!
John Doe - just moments ago
p0wned
Ok
with CSP
Hi you!
John Doe - just moments ago
Content-Security-Policy:
script-src 'self'
https://cdn.example.com
inline scripts are blocked unless
unsafe-inline is specified
script-src
object-src
style-src
img-src
media-src
frame-src
font-src
connect-src
$ curl --head https://twitter.com
HTTP/1.1 200 OK
content-length: 58347
content-security-policy: …
report-uri https://twitter.com/csp_report
violation reports:
"csp-report": {
"document-uri":
"http://example.org/page.html",
"referrer":
"http://evil.example.com/haxor.html",
"blocked-uri":
"http://evil.example.com/image.png",
"violated-directive": "default-src 'self'",
"effective-directive": "img-src",
"original-policy":
"default-src 'self';
report-uri http://example.org/..."
}
support for inline scripts
Content-Security-Policy:
script-src 'sha256-YWIzOW...'
Strict Transport Security
aka HSTS
Strict Transport Security
aka HSTS
mechanism for preventing
HTTPS to HTTP downgrades
telling the browser that your site
should never be reached over HTTP
GET bank.com.au 301→
GET https://bank.com.au 200→
no HSTS, no sslstrip
GET bank.com.au → 200
no HSTS, with sslstrip
what does HSTS look like?
$ curl -i https://login.xero.com
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Strict-Transport-Security: max-age=31536000
X-Frame-Options: SAMEORIGIN
with HSTS, with sslstrip
GET https://bank.com.au 200→
silent client-side redirects
HTTP → HTTPS
no HTTP traffic for
sslstrip to tamper with
except for the very
first connection
https://hstspreload.appspot.com/
pop quiz!
how many .au sites are
on the preload list?
aurainfosec.com.au
bcm.com.au
bigbrownpromotions.com.au
comssa.org.au
data.qld.gov.au
dreamsforabetterworld.com.au
dylanscott.com.au
fatzebra.com.au
freethought.org.au
netrider.net.au
publications.qld.gov.au
technotonic.com.au
thomastimepieces.com.au
tracktivity.com.au
tradingcentre.com.au
webandwords.com.au
16
aurainfosec.com.au
bcm.com.au
bigbrownpromotions.com.au
comssa.org.au
data.qld.gov.au
dreamsforabetterworld.com.au
dylanscott.com.au
fatzebra.com.au
freethought.org.au
netrider.net.au
publications.qld.gov.au
technotonic.com.au
thomastimepieces.com.au
tracktivity.com.au
tradingcentre.com.au
webandwords.com.au
2015?
https://ajax.googleapis.com
/ajax/libs/jquery/1.8.0/
jquery.min.js
how common is this?
what would happen if that
server were compromised?
Bad Things™
steal sessions
leak confidential data
redirect to phishing sites
enlist DDoS zombies
simple solution
instead of this:
<script
src=”https://ajax.googleapis.com...”>
<script
src=”https://ajax.googleapis.com...”
integrity=”sha256-1z4uG/+cVbhShP...”>
do this:
You owe me $10.00.
f4243c12541be6f79c73e539c426e07a
f2f6c4ef8794894f4903aee54542586d
You owe me $1000.
1ebd7a8d15a6dab743f0c4d147f731bc
fc6b74752afe43afa5389ba8830a2215
guarantee:
script won't change
or it'll be blocked
limitation:
won't work for scripts
that change all the time
3 types of scripts
dynamically-generated script:
not a good fit for SRI
I
immutable scripts:
perfect for SRI
II
https://ajax.googleapis.com
/ajax/libs/jquery/1.8.0/
jquery.min.js
what about your own scripts?
(they change, but you're
the one changing them)
III
scripts under your control:
good fit for SRI
can usually add the hashing to
your static resource pipeline
#!/bin/sh
cat src/*.js > bundle.js
HASH=`sha256sum bundle.js |cut -f1 -d' '`
mv bundle.js public/bundle-${HASH}.js
public/bundle-c2498bc358....js
Cache-Control: max-age=∞
<script src=”widgets.js”>
<script src=”app.js”>
<script src=”menu.js”>
<script src=”bundle-c2498bc....js”>
<script src=”bundle-c2498bc....js”
integrity=”sha256-c2498bc...”>
what else?
integrity=”
sha256-9Cm9ekBvKrtQ0A...
“ sha256-rKSr3LcX+EkeM=...
sha256-1z4uG/+cVbhShP...
sha384-RqG7UC/QK2TVRa...
sha512-AODL7idgffQeNs...
”
integrity=”
sha256-9Cm9ekBvKrtQ0A...
sha256-rKSr3LcX+EkeM=...
sha256-1z4uG/+cVbhShP...
“ sha384-RqG7UC/QK2TVRa...
sha512-AODL7idgffQeNs...
”
integrity=”
sha256-9Cm9ekBvKrtQ0A...
sha256-rKSr3LcX+EkeM=...
sha256-1z4uG/+cVbhShP...
sha384-RqG7UC/QK2TVRa...
sha512-AODL7idgffQeNs...
”
what about cross-origin requests?
“a web browser permits scripts contained in a first
web page to access data in a second web page,
but only if both web pages have the same origin”
same-origin policy
example.com/index.html
example.com/index.html
example.com/data.js:
var secret = 42;
example.com/index.html
example.com/data.js:
var secret = 42;
evil.net/widget.js:
exfiltrate(secret);
example.com/index.html
example.com/data.js:
var secret = 42;
evil.net/widget.js:
exfiltrate(secret);
on the server:
Access-Control-Allow-Origin: *
on the server:
Access-Control-Allow-Origin: *
on the client:
crossorigin=”anonymous”
<script
src=”https://ajax.googleapis.com...”
integrity=”sha256-1z4uG/+cVbhShP...”
crossorigin=”anonymous”>
complete example:
<link rel="stylesheet"
href="style.css"
integrity="sha256-PgMdguwx/O..."
crossorigin=”anonymous”>
complete example:
cat file.js
| openssl dgst -sha256 -binary
| openssl enc -base64 -A
SRIhash.org
status?
spec is being finalized
(initial implementations)
demo
<html>
<head>
<title>Bug 992096 - Implement SRI</title>
<link rel="stylesheet"
href="http://localhost/francois/sri/style.css"
integrity="
sha256-PgMdguwx/O1ZJKqtGj54HIScoj0UEDV4ti5tLuc4DvA="
crossorigin="anonymous">
</head>
<body>
<h1>This should be red if the hash matches!</h1>
</body>
</html>
h1 {
color: red;
}
<html>
<head>
<title>Bug 992096 - Implement SRI</title>
<link rel="stylesheet"
href="http://localhost/francois/sri/style.css"
integrity="
sha256-bogus"
crossorigin="anonymous">
</head>
<body>
<h1>This should be red if the hash matches!</h1>
</body>
</html>
Questions?
feedback:
francois@mozilla.com
mozilla.dev.security
public-webappsec@w3.org
© 2015 François Marier <francois@mozilla.com>
This work is licensed under a
Creative Commons Attribution-ShareAlike 4.0 License.
photo credits:
bank notes: https://www.flickr.com/photos/epsos/8463683689
web devs: https://www.flickr.com/photos/mbiddulph/238171366
explosion: https://www.flickr.com/photos/-cavin-/2313239884/

Integrity protection for third-party JavaScript