PageRenderTime 51ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/absolute/index.html

https://github.com/koto/blog-kotowicz-net-examples
HTML | 321 lines | 307 code | 14 blank | 0 comment | 0 complexity | 89c6983f2509ea13226d49afea785179 MD5 | raw file
  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <script src="jquery-1.5.1.min.js"></script>
  6. <script src="jquery.tmpl.min.js"></script>
  7. <script src="jquery.json-2.2.min.js"></script>
  8. <script src="md5.js"></script>
  9. <script>
  10. var payloads, method = 'image'; // there are also 'iframe' and 'redirect' methods available. switch in your JS console.
  11. var load_payloads = function() {
  12. $('#tests').empty();
  13. $('#images').empty();
  14. $.getJSON('payload.json', function(json) {
  15. payloads = json; // store in global var
  16. var li, payload, i;
  17. for (i = 0; i<json.length; i++) {
  18. payload = json[i].data;
  19. li = $('#item-template')
  20. .tmpl(json[i])
  21. .find('code.data').text($.toJSON(payload)).end()
  22. .attr('id', 'p-' + hex_md5(payload))
  23. .appendTo('#tests');
  24. }
  25. $("#start").removeAttr('disabled');
  26. });
  27. };
  28. window.onload = load_payloads;
  29. $(function() {
  30. function log(msg) {
  31. var cont = $('#log')[0];
  32. cont.value += msg + "\n";
  33. cont.scrollTop = 100000;
  34. if (typeof console !== 'undefined' && typeof console.log !== 'undefined') {
  35. console.log(msg);
  36. }
  37. }
  38. function mark(realurl, url, classname) {
  39. var hash = hex_md5(url);
  40. log($.toJSON(url) + ' is ' + classname);
  41. $(document.getElementById('p-' + hash))
  42. .addClass(classname)
  43. .find('.result').text($.toJSON(realurl));
  44. }
  45. var internalDomain = new RegExp(document.location.host);
  46. function checkUrl(realurl, url) {
  47. if (realurl.match(internalDomain)) {
  48. mark(realurl, url, 'internal');
  49. } else {
  50. mark(realurl, url, 'external');
  51. }
  52. }
  53. function checkIframe(realurl, url) {
  54. try {
  55. if (this.contentWindow.document) { // check for SOP restrictions
  56. mark(realurl, url, 'internal');
  57. } else {
  58. mark(realurl, url, 'external');
  59. }
  60. } catch (e) {
  61. mark(realurl, url, 'external');
  62. }
  63. }
  64. $(document).ajaxError(function(event, xhr, options, error) {
  65. log('AJAX error ' + error);
  66. });
  67. function testPayload(payload, id) {
  68. id = id ? '#' + id : '';
  69. log(id + ':Trying ' + $.toJSON(payload));
  70. switch (method) {
  71. case 'image':
  72. // method 1 - img
  73. $('<img>')
  74. .load(function() {
  75. log(id + ':Image source is ' + $.toJSON(this.src));
  76. this.title = this.src;
  77. checkUrl(this.src, $(this).data('src'));
  78. })
  79. .error(function() {
  80. log(id + ':Image ' + $.toJSON(this.src) + " failed to load.");
  81. checkUrl(this.src, $(this).data('src'));
  82. })
  83. .data('src', payload)
  84. .attr('src', payload)
  85. .appendTo('#images');
  86. break;
  87. case 'iframe':
  88. // method 2 - iframe
  89. $('<iframe>')
  90. .load(function() {
  91. log(id + ':Iframe source is ' + $.toJSON(this.src));
  92. this.title = this.src;
  93. checkIframe.apply(this, [this.src, $(this).data('src')]);
  94. })
  95. .error(function() {
  96. log(id + ':Iframe ' + $.toJSON(this.src) + " failed to load.");
  97. checkIframe.apply(this, [this.src, $(this).data('src')]);
  98. })
  99. .data('src', payload)
  100. .attr('src', payload)
  101. .appendTo('#images');
  102. break;
  103. case 'redirect':
  104. // method 3 - redirection
  105. $('<iframe>')
  106. .load(function() {
  107. log(id + ':Iframe source is ' + $.toJSON(this.src));
  108. this.title = this.src;
  109. checkIframe.apply(this, [this.src, $(this).data('src')]);
  110. })
  111. .error(function() {
  112. log(id + ':Iframe ' + $.toJSON(this.src) + " failed to load.");
  113. checkIframe.apply(this, [this.src, $(this).data('src')]);
  114. })
  115. .data('src', payload)
  116. .attr('src', 'redirect.php?url=' + encodeURIComponent(payload))
  117. .appendTo('#images');
  118. break;
  119. }
  120. }
  121. $('#test-manual').click(function() {
  122. var val = $('#url')[0].value;
  123. if (val.indexOf('"') === 0) // JSON literal
  124. try {
  125. val = $.evalJSON(val);
  126. } catch(e) {
  127. alert('Invalid JSON string, use "http://etc/" JSON format or don\'t start with a quote.');
  128. return;
  129. }
  130. testPayload(val);
  131. });
  132. $("#clear").click(function() {
  133. $('#log').val('');
  134. $('#images').empty();
  135. });
  136. $('code.data').live('click', function() {
  137. $('#url').val($(this).text());
  138. testPayload($.evalJSON($(this).text()));
  139. return false;
  140. });
  141. $('#save').click(function() {
  142. if (!payloads) {
  143. alert("You have to start the tests first.");
  144. return;
  145. }
  146. var browser = prompt("Your browser is (firefox,chrome,opera,internet explorer,...): ")
  147. ,version = prompt("Your browser version is (3.6,4,9,...): ")
  148. ,changes = 0
  149. ,update_json = {add: [], remove: [], 'browser': browser, 'version': version, 'method': method};
  150. if (!browser || !version) {
  151. alert('You need to give browser and version to submit results');
  152. }
  153. $('.item').each(function() {
  154. var id
  155. ,offset
  156. ,$this = $(this)
  157. , i;
  158. if (!(id = $this.data('id'))) {
  159. return;
  160. }
  161. for (i in payloads) {
  162. if (payloads.hasOwnProperty(i) && payloads[i].id == id) {
  163. payloads[i].browsers[browser] = payloads[i].browsers[browser] || [];
  164. if ($this.hasClass('external')) { // mark as vulnerable
  165. if ($.inArray(version, payloads[i].browsers[browser]) === -1) {
  166. update_json.add.push(parseInt(id,10));
  167. payloads[i].browsers[browser].push(version);
  168. changes++;
  169. }
  170. }
  171. if ($this.hasClass('internal')) { // remove marking as vulnerable
  172. if ((offset = $.inArray(version, payloads[i].browsers[browser])) !== -1) {
  173. update_json.remove.push(parseInt(id,10));
  174. payloads[i].browsers[browser].splice(offset, 1);
  175. changes++;
  176. }
  177. }
  178. if (payloads[i].browsers[browser].length === 0) {
  179. delete payloads[i].browsers[browser];
  180. }
  181. }
  182. }
  183. });
  184. alert(changes + ' changes.');
  185. if (changes > 0) {
  186. $("#changes").remove();
  187. $("<textarea id=changes style='width:100%; height:50px;'>").val('// send me to kkotowicz at gmail dot com\n\n' + $.toJSON(update_json)).prependTo('body');
  188. }
  189. });
  190. $("#start").click(function() {
  191. $('.result').empty();
  192. $('#images').empty();
  193. $('*').removeClass('external').removeClass('internal');
  194. for (i = 0; i<payloads.length; i++) {
  195. testPayload(payloads[i].data, payloads[i].id);
  196. }
  197. });
  198. });
  199. </script>
  200. <link rel=stylesheet href=styles/basic.css>
  201. <style>
  202. #log {
  203. display: block;
  204. width: 80%;
  205. height: 100px;
  206. }
  207. img {border: 2px solid white;}
  208. code {white-space: pre; font-family: Courier, monospaced; }
  209. .internal .result {color: green;}
  210. .external .result {color: red; }
  211. .internal h4 {background-color: green;}
  212. .external h4 {background-color: red; }
  213. #tests { clear: both; margin: 10px 0; padding: 0;}
  214. #images {opacity: 0.7; position: fixed; top: 0px; right: 10px; text-align: right; width: 70%;}
  215. iframe {width: 24px; height: 24px; padding: 0; border 1px solid #ccc;}
  216. </style>
  217. </head>
  218. <body>
  219. <h1>Local or external?
  220. <small>Weird URL formats on the loose</small></h1>
  221. <p><strong>By <a href="http://blog.kotowicz.net">Krzysztof Kotowicz</a></strong></p>
  222. <p>This page tries to both test & document differences among browsers as to which incomplete or weird URL formats effectively transform
  223. to <strong style="background:green;color:white"> local URLs (being in the same domain as the main page) </strong> or <strong style="background:red;color:white"> external URLs (different domain or protocol) </strong>.
  224. This distinction is very important from security point of view when you are accepting URLs in user input and process them, for example by adding CSRF tokens to the URLs or redirecting the browser.
  225. </p>
  226. <p>
  227. Developers incorrectly assume that external URLs have to start with "http" like "http://example.com", and URLs not starting with that string are considered local &amp; safe. Hovewer, that is far from true.
  228. This page lists all uncommon URL formats that some (or all) browsers recognize as external. To start the test in your browser as well, press <strong>Start tests</strong>.
  229. Items marked red are external for your browser. You can also click on any payload to test it or test any URL manually.
  230. </p>
  231. <p>You can also test any URL format manually.</p>
  232. <p><strong>You can help!</strong>
  233. <ul>
  234. <li><strong>Submit your test results</strong> by clicking "Submit test results". This will check your test results with ours and display the differences in a separate textarea. Mail these differences to kkotowicz at gmail dot com</li>
  235. <li><strong>Found new URL format?</strong> Let me know by email or <a href="http://twitter.com/kkotowicz">@kkotowicz</a>, I'll include it in a test suite!</li>
  236. </ul>
  237. <p>Disclaimer: This test page borrows stylesheet, page layout &amp; other ideas from <a href="http://heideri.ch">Mario Heiderich's</a> excellent <a href="http://heideri.ch/jso">HTML5 Security Cheatsheet</a>. The original work is licenced under <a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser GPL</a> / <a href="http://creativecommons.org/licenses/by/3.0/">CC 3.0 BY</a> and I leave it so. The source code is at <a href="https://github.com/koto/blog-kotowicz-net-examples/tree/master/absolute">GitHub</a>.
  238. </p>
  239. <p>
  240. <button id=start disabled><strong>Start tests</strong></button> <button id=clear>Clear logs</button> <button id=save>Submit test results</button>
  241. </p>
  242. <label>Test URL manually (use plain URL or JSON <a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#String_Literals">string literal</a> in quotes for special characters): <textarea id=url rows=1 cols=50>"http:\u0001google.com"</textarea>
  243. <button id=test-manual>Test</button></label>
  244. <div id=test-data>
  245. <textarea id=log></textarea>
  246. <div id=images></div>
  247. </div>
  248. <ul id=tests></ul>
  249. <script type="text/x-jquery-tmpl" id=item-template>
  250. <li class="item" data-id="${id}">
  251. <a name="${id}"></a>
  252. <h4 class="name">{{if name}}${name}{{else}}${data}{{/if}}<a href="#${id}">#${id}</a></h4>
  253. <p class="description">${description}</p>
  254. {{if redirect_notes}}
  255. <p class="description"><strong>Redirect notes:</strong> ${redirect_notes}</p>
  256. {{/if}}
  257. <code title="Click to test manually" class="data"></code>
  258. <p class="clear">External in:</p>
  259. <ul class="browsers">
  260. {{if browsers.opera}}
  261. <ul class="opera">{{each browsers.opera}}<li>Opera ${$value}</li>{{/each}}</ul>
  262. {{/if}}
  263. {{if browsers.firefox}}
  264. <ul class="firefox">{{each browsers.firefox}}<li>Firefox ${$value}</li>{{/each}}</ul>
  265. {{/if}}
  266. {{if browsers.chrome}}
  267. <ul class="chrome">{{each browsers.chrome}}<li>Chrome ${$value}</li>{{/each}}</ul>
  268. {{/if}}
  269. {{if browsers.safari}}
  270. <ul class="safari">{{each browsers.safari}}<li>Safari ${$value}</li>{{/each}}</ul>
  271. {{/if}}
  272. {{if browsers["internet explorer"]}}
  273. <ul class="internet explorer">{{each browsers['internet explorer']}}<li>Internet Explorer ${$value}</li>{{/each}}</ul>
  274. {{/if}}
  275. {{if browsers.android}}
  276. <ul class="android">{{each browsers.android}}<li>Android ${$value}</li>{{/each}}</ul>
  277. {{/if}}
  278. </ul>
  279. <p class="clear">Result in this browser:</p>
  280. <code class="result">image not loaded</code>
  281. {{if urls}}
  282. <ul class="urls clear">
  283. {{each urls}}<li><a href="${$value}">${$value}</a>{{/each}}
  284. </ul>
  285. {{/if}}
  286. <dfn class="reporter">${reporter}</dfn>
  287. </li>
  288. </script>
  289. </body>
  290. </html>