XHR XSS ã®è©±ï¼
æ¦è¦
CORS ããå¹¾ã¤ãã®ãã©ã¦ã¶ã®å è¡å®è£ ãã®ç¶æ³ãããå¤ããã©ã¦ã¶ã§ã¯ãµãã¼ããããªãæ©è½ãã«å¤ããã¤ã¤ããé åããªã®ã§ï¼XHR2 ã XSS ã®èµ·ç¹ã«ãªãã¾ããã£ã¦ã話ï¼
ãããã XHR XSS ã£ã¦ä½ã
ç°¡åã«è¨ãã¨XHR2 ã«ãã XSS ã®ãã¨ã®ã¤ããï¼èº«è¿ãªã¨ããã ã¨ï¼jQuery Mobile ãããããããï¼å¤§éªåºè¦ãããããããããï¼
å ·ä½ä¾1 jQuery Mobile
jQuery Mobile ã«ã¤ãã¦ã¯ï¼jQuery Mobileã®XSSã«ã¤ãã¦ã®è§£èª¬ ã§è§£èª¬ãããã¨ããï¼
ããã¤ã¾ãã§è¨ãã¨ï¼jQuery Mobile ã« location.hash ã®å¤æ´( hashchange ã¤ãã³ãçºç«ï¼æã«ï¼location.hash ã URL ã¨ã¿ãªãã¦èªè¾¼ãã§ï¼ãã¼ã¸å 容ã夿´ã¨ããæ©è½ããã£ã¦ï¼ãã®èªè¾¼å URL ã«ã¯ãã¹ãã¡ã¤ã³ã®å¶ç´ããªãã£ãã®ã§ XHR2 å¯è½ãªç°å¢ã§ã¯ï¼location.hash ã« XHR2 ã§èªè¾¼ã¿å¯è½ãª URL ãæå®ãã¦ããã¨ãã®ãã¼ã¸ãèªã¿è¾¼ãã ã ãã§ï¼æªæããã³ã³ãã³ãã«æ±æãããã¨ããã¢ãï¼
beta2 ã§ã¯ã¯ãã¹ãã¡ã¤ã³ã§ã®èªè¾¼ã¿ã«æç¤ºçãªè¨±å¯ãã©ã°ã®è¨å®ãå¿ è¦ã«ãªã£ã¦ããã®ã§ã²ã¨ã¾ãã¯åæãã¦ããï¼
å ·ä½ä¾2 大éªåºè¦ã®è©±
大éªåºè¦ã®ãµã¤ãã§ã¯ï¼Prototype.js ã® Ajax ãç¨ã㦠jQuery Mobile ã®ä¾ã®æ§ãªãã¼ã¸é·ç§»ã®ãªãå 容ã®åãæ¿ããå®è£ ããã¦ããï¼ï¼2012/03 ç¾å¨ï¼
ãã®å
容ã®åãæ¿ãå
ã®URL ã«ä¸å¿ã®å¶ç´ï¼fromHash.match(/^[a-z_\-\.\/]+$/i)
ï¼ããã£ããï¼'//evil.example.com/foobar' ã®ããã« '//' ããã¯ããããã¨ã§åãæ¿ãå
ãä»»æã® URL ã«ãããåé¡ããã£ãï¼
PoC ãå ¬éããã¦ããã«ï¼åãæ¿ãå ã '//' ãå«ã¾ãï¼ãã¤ãã¯ã¤ããªã¹ãã«å®å ¨ä¸è´ããå ´åã«ã ãé·ç§»ããããã«ä¿®æ£ããåæããï¼
CORS æä»£ã® XHR ã¨ã®ãä»ãåã
å
ã
ãã¡ã¤ã³ãè¶
ãããªã½ã¼ã¹ã¸ã®ã¢ã¯ã»ã¹ã®éè¦ã«ã¯ï¼ã¡ã¸ã£ã¼ãªãã©ã¦ã¶ã§ã¯ IE ã 10pp4 ( 2011å¹´11ææ« ) ã¾ã§ï¼Opera ã 12.00 (Presto/2.10.232) ã¾ã§å¯¾å¿ãã¦ãªãï¼JSONPï¼iframe ã«ããä»£æ¿ææ³ã®ä¾çµ¦ã IE8 ã§ã®å°ç¨ API ã® XDocumentRequest
ã®åå¨ã«ããï¼ã¯ãã¹ãã¡ã¤ã³ã®ãªã¯ã¨ã¹ããæèçã«å¥ç©ã¨ãã¦æ±ã£ã¦æ¥ãçµç·¯ãããï¼
ãã®ãã¨ã XHR.open()
ã®ãªã¯ã¨ã¹ãå
URL ã«å¯¾ãã¦ï¼jQuery Mobile ã®ããã«ï¼ããããæèããã¦ãªãã£ããï¼åºè¦ã®ä¾ã®æ§ã« '//' ããå§ã¾ã URL ããã¡ã¤ã³ãè¶
ããã¢ã¯ã»ã¹ã«ãªããã¨ãè¦è½ã¨ããããã¨ãã£ãç¶æ³ãç£ãã§ããããã«æãï¼
ãããã©ããããã¨ããã¨ï¼å¹¾ã¤ãã®ç¶æ³ã«å¿ãã¦å¯¾å¿ãã¦ããå¿ è¦ãããï¼
å¤é¨ã®APIã¸ã®ãªã¯ã¨ã¹ããåºãå ´å
XHR2 ã§å¤é¨ã® API ã®ãªã½ã¼ã¹ãåå¾ããå ´åã¯ï¼æ³å®ãã API ã§ã¯ãªã URL ã¸ã®ãªã¯ã¨ã¹ãã«ãªã£ã¦ãªããã«æ°ãã¤ããï¼
ä¾ãã°ï¼ 以ä¸ã®æ§ãªã³ã¼ãã§ãã£ã¬ã¯ããªãã©ãã¼ãµã«ãæ¤åºãããªã©ããæ¹ã¯è²ããããã¨æãï¼
var apiurl = 'http://api.example.org/some/api/point/' + cook(params); var absurl = (function(path){ var a = document.createElement('a'); a.href = path; return a.href;})(apiurl); if ( apiurl != absurl ) { alert('something wrong'); }
åä¸ãªãªã¸ã³ã¸ã®ãªã¯ã¨ã¹ããåºãå ´å
åä¸ãªãªã¸ã³ã¸ã®ãªã¯ã¨ã¹ãããè¡ããªãã¨ãã§ãï¼å¤é¨ã® API ã¸ã®ãªã¯ã¨ã¹ãã®ããã«ï¼ãªã¯ã¨ã¹ãå ãã¡ã¤ã³ãå¤é¨ãã夿´ã§ããªããããªç®æã«å ¥ãã¦ãã㦠http://... ããå§ã¾ã url ãçµã¿ç«ã¦ãã°è¯ãï¼
ä¾ãã°ï¼
function xhrget( path ){ var xhr = new XMLHttpRequest(); xhr.open( 'GET', path ); /* more code... */ }
ãªã©ãªã£ã¦ããã®ã
function xhrget( path ){ var xhr = new XMLHttpRequest(), absurl = 'http://self.example.com/' + path; /* or absurl = [location.protocol, '//', location.hostname, location.port ? ':' : '', location.port, path ].join( '' ); */ xhr.open( 'GET', absurl ); /* more code... */ }
ã¨ããããã«ï¼ãªã¯ã¨ã¹ãå ãã¡ã¤ã³ããªãã©ã«ã§æå®ããã location ã® hash ã search 以å¤ã®ç®æããçµã¿ç«ã¦ããããã° path ã 'http:' ã '//' ããå§ã¾ã£ã¦ãã¦ãæå³ããªãå¤é¨ãªã½ã¼ã¹ã¸ã®ã¢ã¯ã»ã¹ã¯çºçããªããªãï¼
ã¦ã¼ã¶ã¼å ¥åã®ä»»æã® URL ã«å¯¾ã㦠XHR ãããå ´å
html ãåå¾ãã¦å©ç¨ããå ´åãªãï¼XHR ResponseTypeã document
ã«ãã¦åå¾ãããã¨ã§ script ãåãé¤ããã¦åä½ãã¦ãªãç¶æ
ã® DOM ãæã«å
¥ããããã®ã§ï¼ããã§ä½ã¨ãããï¼ResponseType ããµãã¼ããã¦ãªãå ´åãï¼ãã以å¤ã®å½¢å¼ã®ãªã½ã¼ã¹ï¼ä¾ãã°ä»»æã® JSON ãåå¾ãã¦ã©ãã«ãããã¨ãã¯çæ°ã®æ²æ±°ãªã®ã§ä¸ç¶ãµã¼ããæãã¨ãã£ããã©ã¦ã¶ã®ç¯å²å¤ã§å¯¾å¿ããã®ããã¿ã¼ã¨æãï¼
ã¾ã¨ã
IE10 ã Opera 12 ã®ç»å ´ã§ã¢ãã¤ã«ç«¯æ«ããã§ã«ããã§ããããã« XHR ããã¡ã¤ã³ãè¶ ããããããã«ãªãã®ã§ï¼ãªã¯ã¨ã¹ãå ã«ã¯ååæ³¨æï¼
ä»»æã®æååãçªã£è¾¼ããããã«ãªã£ã¦ãã¨å½ç¶ã«XSS ã¨ãªããï¼çµ¶å¯¾ãã¹ã渡ãã¤ããã§ '/' ããå§ã¾ãããã«ãã¦ã '//' ããå§ã¾ãããã«ãããã¨ããã XSS ã«ãªãã®ã§ï¼å¤é¨ãã夿´ä¸è½ãªå½¢ã§ãã¡ã¤ã³ãè¨å®ãã¦ãã XHR.open ã«æ¸¡ãããã«ï¼
ä»»æã® URL ã¸ã® XHR2 ãå®å ¨ã«ä½¿ãã®ã¯ç¡çã²ã¼ãªã®ã§ãããï¼
追è¨
JSON ã«ä½ã®åé¡ãããã®ã¨ææããããã®ã§æ´çã¨è¿½è¨ï¼
ãã¼ãªã®ï¼JSON.parseãããã°ããããããªãã®ããï¼ ⇒ "ä¾ãã°ä»»æã® JSON ãåå¾ãã¦ã©ãã«ãããã¨ãã¯çæ°ã®æ²æ±°" t-ashula.hateblo.jp/entry/2012/02/…
— Shinichi Tomitaãã (@stomita) 3æ 16, 2012
ã¡ãã£ã¨ããããããªããXHRã§ãªã¯ã¨ã¹ããçºè¡ããããã¨ããèªä½ã¯XSSã¨é¢ä¿ãªãããã®å¾ãhtmlçæãªã©ã§DOMç ´å£ããã®ãXSSãXDã§JSONã§åãåã£ã¦ãåé¡ãªãã¨æãã / XHR XSS ã®è©±ï¼ - ã»ããã¡ãã»ãã»ã t-ashula.hateblo.jp/entry/2012/02/…
— Yosuke HASEGAWAãã (@hasegawayosuke) 3æ 16, 2012
XHR XSS ã¨æ¸ãããï¼XSS ã¨ãªãã®ã¯ä»¥ä¸ã®åææ¡ä»¶ãã¹ã¦æºããå¿ è¦ãããï¼
- ãã¼ã¸ã®é²è¦§è ãå¶å¾¡å¯è½ãªç®æãã XHR ã®å®å ¨ãªãªã¯ã¨ã¹ãå ãæ±ºãã¦ãããã¨
- XHR ã§åå¾ãããªã½ã¼ã¹ãåå¾å ã®ãªã½ã¼ã¹ã® DOM ãã¹ã¯ãªããã®æåã«å½±é¿ãä¸ããæ¹æ³ã§æ³¨å ¥ãã¦ãããã¨
jQuery Mobile ã®å ´åã¯ï¼ãã®ã©ã¡ããããã¬ã¼ã ã¯ã¼ã¯ãå ¨èªåã§æºããã¦ãããããã¬ã¼ã ã¯ã¼ã¯ãç¨ãã¦ãããã¹ã¦ã®ãã¼ã¸ãç´°å·¥ããã URL çµç±ã§èªã¿è¾¼ãã ãã§ï¼XSS ã«ãªã£ã¦ãï¼å¤§éªåºè¦ã®ä¾ã®å ´å㯠location.hash ãããªã¯ã¨ã¹ãå ãä½ã£ã¦ãããã¨ã§1ã¤ç®ã®æ¡ä»¶ãæºãããï¼åå¾ãããªã½ã¼ã¹ãæååã¨ãã¦å å·¥ããå¾ HTML ã¨ãã¦å ã®ãã¼ã¸ã® DOM ã«æ³¨å ¥ãã¦ãããã¨ã§2ã¤ç®ã®æ¡ä»¶ãæºããã XSS ã«ãªã£ã¦ãï¼
CORS æä»£ã® XHR ã¨ã®ãä»ãåã
ã§æ¸ãããã¨ã®ãã¡ï¼ãæ³å®ãã API ã§ã¯ãªã URL ã¸ã®ãªã¯ã¨ã¹ãã«ãªããªãããã«ããããã¨ãï¼ããªã¯ã¨ã¹ãå
ãã¡ã¤ã³ãå¤é¨ãã夿´ã§ããªãããã«ããããã¨ã¯ï¼XSS ã¨ãªãåææ¡ä»¶ã®ãã¡1ã¤ç®ã®æ¡ä»¶ãæºãããªãããã«ããããã®æªç½®ï¼
å®éã«ã¯ï¼æ³å®ãã URL ã¸ã®ãªã¯ã¨ã¹ãã«ãªãããã«ãã¦ãï¼åææ¡ä»¶ã®2ã¤ç®ãæºãããªãããã«ã¯ãã¦ããªãã®ã§ï¼ãªã½ã¼ã¹ãå©ç¨ããæ®µéã§ API ãå¤ãªã¢ããè¿ãã¦ãã¦ããï¼åä¸ãã¡ã¤ã³å ã®å¤ãªã¢ããæ¾ã£ã¦ãã¾ã£ããã§ï¼ã¹ã¯ãªãããå®è¡ãããå¯è½æ§ã¯ä¾ç¶ã¨ãã¦æ®ãï¼ä»»æã®URL ããåå¾ããå ´åãããã£ã¨å¯è½æ§ã¯ä½ããã©ï¼å®å ¨ã«æ±ãããã®ææ®µãè¬ãã¦ããã»ããããï¼
ä»»æã® URL ã«å¯¾ãã XHR ã§ HTML ãåå¾ããã¨ãã« ResponseType ãæå®ããã¨ããã®ã¯ï¼ãã®è¨äºã§ã®å ·ä½ä¾1,2ã§ç¤ºãããããªãå¥ãã¼ã¸ã®å å®¹ï¼æååï¼ã HTML æçã¨ãã¦ä»ã®ãã¼ã¸ã® DOM ã«æ³¨å ¥ãããå ¸åçãªç¶æ³ã§ï¼XSS ã¨ãªãåææ¡ä»¶ã®2ã¤ç®ãæºãããªãããã«ããããã®æªç½®ï¼ããï¼XHR ã§åå¾ããä»»æã®ãªã½ã¼ã¹ããçæãã DOM ãæ´ã«å å·¥ãã¦ä½å¦ãã«å©ç¨ããå ´åï¼XSS ã¨ãªããã¨ããã
JSON ã®å ´åãï¼ä»»æã® URL ããåå¾ãã¦ã㦠JSON.parse ã§ãªãã¸ã§ã¯ãã«ããã ãã§ã¯ï¼ãã©ã¦ã¶ã«ãã°ãããã°å¥ã ãï¼ï¼åææ¡ä»¶ã®2ã¤ç®ãæºããã¦ãªãã®ã§ XSS ã¨ãªããªãï¼åå¾å
ãã¼ã¸ã§å å·¥ãå©ç¨ããéã«ï¼XSS ã«ãªãå¯è½æ§ã¯ HTML ãåå¾ãã¦ããå ´åãï¼èªãã¡ã¤ã³ããã®åå¾ã®å ´åã¨åæ§ã«åå¨ããï¼JSON.parse ããã°ããã®ã§ã¯
ãªããï¼ã¯ãã¹ãã¡ã¤ã³ã§ JSON ãåãåã£ã¦ãåé¡ãªãã¨æã
ã¨ããã®ã¯ãã®éãã§ï¼åå¾ã ãã§ XSS ã«ãªããã®ãããªãçæ°ã®æ²æ±°ãã¨æ¸ãã¦ãã®ã¯è¨ãããã ã£ãã¨æãï¼