[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 @title Rendering HTML 2 @group developer 3 4 Rendering HTML in the Phabricator environment. 5 6 = Overview = 7 8 Phabricator attempts to prevent XSS by treating strings as default-unsafe when 9 rendering. This means that if you try to build HTML through string 10 concatenation, it won't work: the string will be escaped by the rendering 11 pipeline, and the browser will treat it as plain text, not HTML. 12 13 This document describes the right way to build HTML components so they are safe 14 from XSS and render correctly. Broadly: 15 16 - Use @{function@libphutil:phutil_tag} (and @{function:javelin_tag}) to build 17 tags. 18 - Use @{function@libphutil:hsprintf} where @{function@libphutil:phutil_tag} 19 is awkward. 20 - Combine elements with arrays, not string concatenation. 21 - @{class:AphrontView} subclasses should return a 22 @{class@libphutil:PhutilSafeHTML} object from their `render()` method. 23 - @{class:AphrontView} subclasses act like tags when rendering. 24 - @{function:pht} has some special rules. 25 - There are some other things that you should be aware of. 26 27 See below for discussion. 28 29 = Building Tags: phutil_tag() = 30 31 Build HTML tags with @{function@libphutil:phutil_tag}. For example: 32 33 phutil_tag( 34 'div', 35 array( 36 'class' => 'some-class', 37 ), 38 $content); 39 40 @{function@libphutil:phutil_tag} will properly escape the content and all the 41 attributes, and return a @{class@libphutil:PhutilSafeHTML} object. The rendering 42 pipeline knows that this object represents a properly escaped HTML tag. This 43 allows @{function@libphutil:phutil_tag} to render tags with other tags as 44 content correctly (without double-escaping): 45 46 phutil_tag( 47 'div', 48 array(), 49 phutil_tag( 50 'strong', 51 array(), 52 $content)); 53 54 In Phabricator, the @{function:javelin_tag} function is similar to 55 @{function@libphutil:phutil_tag}, but provides special handling for the 56 `sigil` and `meta` attributes. 57 58 = Building Blocks: hsprintf() = 59 60 Sometimes, @{function@libphutil:phutil_tag} can be particularly awkward to 61 use. You can use @{function@libphutil:hsprintf} to build larger and more 62 complex blocks of HTML, when @{function@libphutil:phutil_tag} is a poor fit. 63 @{function:hsprintf} has `sprintf()` semantics, but `%s` escapes HTML: 64 65 // Safely build fragments or unwieldy blocks. 66 hsprintf( 67 '<div id="%s">', 68 $div_id); 69 70 @{function:hsprintf} can be especially useful when: 71 72 - You need to build a block with a lot of tags, like a table with rows and 73 cells. 74 - You need to build part of a tag (usually you should avoid this, but if you 75 do need to, @{function@libphutil:phutil_tag} can not do it). 76 77 Note that it is unsafe to provide any user-controlled data to the first 78 parameter of @{function@libphutil:hsprintf} (the `sprintf()`-style pattern). 79 80 Like @{function@libphutil:phutil_tag}, this function returns a 81 @{class@libphutil:PhutilSafeHTML} object. 82 83 = Composing Tags = 84 85 When you are building a view which combines smaller components, like a section 86 with a header and a body: 87 88 $header = phutil_tag('h1', ...); 89 $body = phutil_tag('p', ...); 90 91 ...you should NOT use string concatenation: 92 93 COUNTEREXAMPLE 94 // Not dangerous, but does the wrong thing. 95 phutil_tag('div', array(), $header.$body); 96 97 Instead, use an array: 98 99 // Render a tag containing other tags safely. 100 phutil_tag('div', array(), array($header, $body)); 101 102 If you concatenate @{class@libphutil:PhutilSafeHTML} objects, they revert to 103 normal strings and are no longer marked as properly escaped tags. 104 105 (In the future, these objects may stop converting to strings, but for now they 106 must to maintain backward compatibility.) 107 108 If you need to build a list of items with some element in between each of them 109 (like a middot, comma, or vertical bar) you can use 110 @{function:phutil_implode_html}: 111 112 // Render links with commas between them. 113 phutil_tag( 114 'div', 115 array(), 116 phutil_implode_html(', ', $list_of_links)); 117 118 = AphrontView Classes = 119 120 Subclasses of @{class:AphrontView} in Phabricator should return a 121 @{class@libphutil:PhutilSafeHTML} object. The easiest way to do this is to 122 return `phutil_tag()` or `javelin_tag()`: 123 124 return phutil_tag('div', ...); 125 126 You can use an @{class:AphrontView} subclass like you would a tag: 127 128 phutil_tag('div', array(), $view); 129 130 = Internationalization: pht() = 131 132 The @{function:pht} function has some special rules. If any input to 133 @{function:pht} is a @{class@libphutil:PhutilSafeHTML} object, @{function:pht} 134 returns a @{class@libphutil:PhutilSafeHTML} object itself. Otherwise, it returns 135 normal text. 136 137 This is generally safe because translations are not permitted to have more tags 138 than the original text did (so if the original text had no tags, translations 139 can not add any). 140 141 Normally, this just means that @{function:pht} does the right thing and behaves 142 like you would expect, but it is worth being aware of. 143 144 = Special Cases = 145 146 NOTE: This section describes dangerous methods which can bypass XSS protections. 147 If possible, do not use them. 148 149 You can build @{class@libphutil:PhutilSafeHTML} out of a string explicitly by 150 calling @{function:phutil_safe_html} on it. This is **dangerous**, because if 151 you are wrong and the string is not actually safe, you have introduced an XSS 152 vulnerability. Consequently, you should avoid calling this if possible. 153 154 You can use @{function@libphutil:phutil_escape_html_newlines} to escape HTML 155 while converting newlines to `<br />`. You should not need to explicitly use 156 @{function@libphutil:phutil_escape_html} anywhere. 157 158 If you need to apply a string function (such as `trim()`) to safe HTML, use 159 @{method@libphutil:PhutilSafeHTML::applyFunction}. 160 161 If you need to extract the content of a @{class@libphutil:PhutilSafeHTML} 162 object, you should call `getHTMLContent()`, not cast it to a string. Eventually, 163 we would like to remove the string cast entirely. 164 165 Functions @{function@libphutil:phutil_tag} and @{function@libphutil:hsprintf} 166 are not safe if you pass the user input for the tag or attribute name. All the 167 following examples are dangerous: 168 169 counterexample 170 phutil_tag($evil); 171 172 phutil_tag('span', array($evil => $evil2)); 173 174 phutil_tag('span', array('onmouseover' => $evil)); 175 176 // Use PhutilURI to check if $evil is valid HTTP link. 177 hsprintf('<a href="%s">', $evil); 178 179 hsprintf('<%s>%s</%s>', $evil, $evil2, $evil); 180 181 // We have a lint rule disallowing this. 182 hsprintf($evil);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |