Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Secure Escaping method for the age of HTML5 #appsecapac2014

OWASP Japan
March 20, 2014
690

Secure Escaping method for the age of HTML5 #appsecapac2014

OWASP Japan

March 20, 2014
Tweet

Transcript

  1. about:TAKESAKO OWASP JAPAN Advisory Board  2008: Microsoft MVP "Developer

    Security" 2011: SC22/ECMAScript (ISO/IEC 16262) 2012: HTML5 technical review (coauthor) 2013: ECMA-262 Edition 5.1 (en2ja)
  2. Today's agenda XSS is a threat consistently listed in the

    OWASP Top 10  1. to prevent XSS, it is necessary to different appropriate escaping 2. but it cause human error and easily lead to careless mistakes 3. contextual automatic HTML escaping method became a realistic solution with popularization of HTML5 Syntax
  3. ESCAPE, TO BE SECURE 1. History of HTML escaping method

     "PHP" of the good old days  template engines got default escaping 2. Modern HTML escaping method  "contextual" automatic escaping  AngularJS, Closure Templates … 3. HTML5 syntax  parser bugs 4. Excursus Excluding - How to prevent SQL injection - New solution of DOM based XSS
  4. %27 ... single quote <input value='<?php echo htmlspecialchars($x); ?>'> '

    onfocus=alert(1) autofocus ' [Input] $x ...value='' onfocus=alert(1) autofocus ''> [Output] XSS... 
  5. (2) ENT_QUOTES <input value='<?php echo htmlspecialchars($x, ENT_QUOTES); ?>'> ' onfocus=alert(1)

    autofocus ' [Input] $x &#039; onfocus=alert(1) autofocus &#039; [Output] secure? 
  6. %82 ... a multi-byte char <input value='<?php echo htmlspecialchars($x, ENT_QUOTES);

    ?>'><input value=' y %82 [Input] $x <input value='・・・・・・' y [Output] insecure 
  7. (3) valid charset encoding <input value='<?php echo htmlspecialchars($x, ENT_QUOTES, 'UTF-8');

    ?>'><input value=' y %82 [Input] $x <input value=''><input value=' y [Output] secure 
  8. Default all ESCAPE setting {$str} == {$str|escape:"html"} Smarty 2 {$inner_html|smarty:nodefaults}

    Smarty 3 {$inner_html nofilter} $smarty = new Smarty(); $smarty->default_modifiers = array('escape:"html"');
  9. Q. How to ESCAPE ? $x1, $x2, $x3, $x4, $x5,

    $x6, $x7 <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}'; var y = {$x5}; </script> <style> body { background:url(/path?q={$x6}); } div { font-family: "{$x7}"; } </style>
  10. Smarty|escape:"TYPE" TYPE {$str|escape:"TYPE"} 1 html htmlspecialchars($str, ENT_QUOTES, $charset); 2 htmlall

    htmlentities($str, ENT_QUOTES, $charset); 3 quotes '  ¥' 4 url rawurlencode($str); 5 urlpathinfo str_replace('%2F','/',rawurlencode($str)); 6 hex ABC  %41%42%43 7 hexentity ABC  &#x41;&#x42;&#x43; 8 mail str_replace('@',' [AT] '), str_replace('.',' [DOT] '), $str)); 9 javascript ¥¥¥, '¥', "¥", ¥r¥¥r, ¥n¥¥n, </<¥/
  11. There are no "Swiss Army Knife" URL encode HTML escape

    図:彩画職人部類(天明4年)大正期復刻版 より CSS escape
  12. A[ng]ularJS ng-app Declares an element as a root element of

    the application allowing behavior to be modified through custom HTML tags. ng-bind ng-bind-html Automatically changes the text of an HTML element to the value of a given expression. ng-model Similar to ng-bind, but allows two-way data binding between the view and the scope. ng-class Allows class attributes to be dynamically loaded. ng-controller Specifies a JavaScript controller class that evaluates HTML expressions. ng-repeat Instantiate an element once per item from a collection. ng-show ng-hide Conditionally show or hide an element, depending on the value of a boolean expression. Show and hide is achieved by setting the CSS display style. ng-switch Conditionally instantiate one template from a set of choices, depending on the value of a selection expression. ng-view The base directive responsible for handling routes that resolve JSON before rendering templates driven by specified controllers. ng-if Basic if statement directive which allow to show the following element if the conditions are true. When the condition is false, the element is removed from the DOM. When true, a clone of the compiled element is re-inserted.
  13. AngularJS: ng.$sce SCE = Strict Context Escaping <!doctype html> <html

    ng-app> <script src="angular.js"> </script> <input ng-model="x"> <a href="{{x}}"> {{x}}</a>
  14. docs.angularjs.org/api/ng/service/$sce Context Notes $sce.HTML For HTML that's safe to source

    into the application. The ngBindHtml directive uses this context for bindings. $sce.URL For URLs that are safe to follow as links. Currently unused (<a href= and <img src= sanitize their urls and don't constitute an SCE context. $sce.RESOURCE_URL For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application. Examples include ng-include, src / ngSrc bindings for tags other than IMG (e.g. IFRAME, OBJECT, etc.) $sce.CSS For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. $sce.JS For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives.
  15. example.soy (1/2) {namespace example autoescape="contextual"} /** * example of autoescape

    contextual * @param x1 * @param x2 * @param x3 * @param x4 * @param x5 * @param x6 * @param x7 */ {template .render} ... {/template} example.soy (2/2)
  16. example.soy (2/2) {template .render} <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x

    = '{$x4}'; var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style>
  17. Closure Templates SoyToJsSrcCompiler.jar [INPUT] example.soy [OUTPUT] example.js Compiled to JavaScript

    source code > java -jar SoyToJsSrcCompiler.jar --outputPathFormat example.js --srcs example.soy
  18. Result : example.js // This file was automatically generated from

    example.soy. // Please don't edit this file by hand. if (typeof example == 'undefined') { var example = {}; } example.render = function(opt_data, opt_ignored) { return '<a href="' + soy.$$escapeHtmlAttribute(soy.$$filterNormalizeUri(opt_data.x1)) + '" onmouseover="' + soy.$$escapeHtmlAttribute(soy.$$escapeJsValue(opt_data.x2)) + '">' + soy.$$escapeHtml(opt_data.x3) + '</a>' + '<script>var x = ¥'' + soy.$$escapeJsString(opt_data.x4) + '¥'; var y = ' + soy.$$escapeJsValue(opt_data.x5) + ';<¥/script>' + '<style>body{ ' + 'background:url(/path?q=' + soy.$$escapeUri(opt_data.x6) + '); }' + 'div { font-family: "' + soy.$$escapeCssString(opt_data.x7) + '"; }' + '</style>'; };
  19. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style>
  20. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeHtmlAttribute( soy.$$filterNormalizeUri(opt_data.x1) );
  21. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeHtmlAttribute( soy.$$escapeJsValue(opt_data.x2) );
  22. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeHtml(opt_data.x3);
  23. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeJsString(opt_data.x4);
  24. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeJsValue(opt_data.x5); null, true, false, 3.14, 'toString()'
  25. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeUri(opt_data.x6);
  26. Result (autoescape="contextual") <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script> var x = '{$x4}';

    var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style> soy.$$escapeCssString(opt_data.x7);
  27. Substitutions in URLs <a href="{$x}"> Entity-escape and filter out bad

    protocols 1 "http://foo/" → <a href="http://foo/"> 2 "/foo?a=b&c=d" → <a href="/foo?a=b&amp;c=d"> 3 "javascript:alert(1337)" → <a href="#zSoyz"> <a href="/foo/{$x}"> Just entity-escape 4 "bar" → <a href="/foo/bar"> 5 "bar&baz/boo" → <a href="/foo/bar&amp;baz/boo"> <a href="/foo?q={$x}"> Percent encode inside query 6 "bar&baz=boo" → <a href="/foo?q=bar%26baz%3dboo"> 7 new soydata.SanitizedUri ("bar&baz=boo") → <a href="/foo?q=bar&baz=boo"> 8 "A is #1" → <a href="/foo?q=A%20is%20%231"> https://developers.google.com/closure/templates/docs/security
  28. Substitutions in CSS <style>div#{$id} { }</style> Classes and IDs 1

    "foo-bar" → <style>div#foo-bar { }</style> <div style="margin-{$ltr-dir}: 1em"> Property Names 5 "left" → <div style="margin-left: 1em"> 6 "right" → <div style="margin-right: 1em"> <div style="background: url({$x})"> URLs in CSS are handled as in attributes above 9 "/foo/bar" → <div style="background: url(/foo/bar)"> 10 "javascript:alert(1337)" → <div style="background: url(#zSoyz)"> 11 "?q=(O'Reilly) OR Books" → <div style="background: url(?q=%28O%27R eilly%29%20OR%20Books)"> <div style="color: {$x}"> Quantities 2 "red" → <div style="color: red"> 3 "#f00" → <div style="color: #foo"> 4 "expression('alert(1337)')" → <div style="color: zSoyz"> <style>p{font-family:'{$x}'}</style> Quoted Values 7 "Arial" → <style>p{font-family:'Arial'}</style> 8 "</style>" → <style>p{font-family:'¥3c /style¥3e '}</style>
  29. However, HTML parser is valid? <a href="{$x1}" onmouseover="{$x2}">{$x3}</a> <script><!-- --

    broken HTML--- -><!-!> var x = '{$x4}'; var y = {$x5}; </script> <style> body{lb} background:url(/path?q={$x6});{rb} div {lb} font-family: "{$x7}"; {rb} </style>
  30. HTML syntax checker [demo] <!DOCTYPE html> <title>HTML5 Syntax Checker</title> <h2>Your

    Browser is...</h2> <img :src = "opera8.gif" x=``src = "ie.gif" srcZ/="netscape6.gif" src#= "netscape7.gif" = /src = "html5.gif" ''src^K= "safari.gif" /src = "firefox.gif" src = "others.gif" src = "lynx.gif" /> <hr/><!-- ^K is Vertical Tab '¥x0B' --> <p>http://www.w3.org/TR/html5/syntax.html</p>
  31. defined HTML5 Syntax <h2>Your Browser is...</h2> <img :src = "opera8.gif"

    x=``src = "ie.gif" srcZ/="netscape6.gif" src#= "netscape7.gif" = /src = "html5.gif" ''src^K= "safari.gif" /src = "firefox.gif" src = "others.gif" src = "lynx.gif" />
  32. CSS Hacks CSS Hacks must die ...  <style type="text/css">

    body { color: red; /* all browsers */ color: green¥9; /* IE6,7,8 */ *color: yellow; /* IE6,7 */ _color: orange; /* IE6 */ }
  33. set NS_ENABLE_TAINT=1 taint(), untaint() Object Tainted properties and methods document

    cookie, domain, forms[], lastModified, links[], location, referrer, title, URL form action all form input elements: button, checkbox, fileupload, hidden, password, radio, reset, select, submit, text, textarea checked, defaultChecked, defaultValue, name, selectedIndex, toString(), value history current, next, previous,toString(), all array elements location, link, area hash, host, hostname, href, pathname, port, protocol, search, toString() option defaultSelected, selected, text, value window defaultStatus, status
  34. Netscape Navigator 2.0+ navigator.taintEnabled() Mozilla Firefox 28 Opera 15 

    no method Internet Explorer 11 Historical reasons: removed in JavaScript version 1.2
  35. MooTools 1.2.6 browser detect Browser = $merge({ Engine: {name: 'unknown',

    version: 0}, Engines: { presto: function(){ return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }, trident: function(){ return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }, webkit: function(){ return (navigator.taintEnabled) ? false : ((Browser.Features.xpath)?((Browser.Features.query) ? 525 : 420) : 419); }, gecko: function(){ return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); } },
  36. Conclusions 1. Contextual automatic HTML escaping became a new solution

    to prevent XSS 2. HTML5 Syntax HTML5 Parser can be implemented same algorithm in the server-side and client-side 3. Broken HTML5 parser is browser's bug If you find a parse error, please report it  4. Static analysis is very useful But it can not solved the DOM based XSS Needs dynamic data-tainting security model