|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | +<head> |
| 4 | +<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| 5 | +<meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | +<link rel="stylesheet" href="https://best.openssf.org/assets/css/style.css"> |
| 7 | +<link rel="stylesheet" href="checker.css"> |
| 8 | +<script src="js-yaml.min.js"></script> |
| 9 | +<script src="checker.js"></script> |
| 10 | +<link rel="license" href="https://creativecommons.org/licenses/by/4.0/"> |
| 11 | + |
| 12 | +<!-- See create_labs.md for how to create your own lab! --> |
| 13 | + |
| 14 | +<!-- Sample expected answer --> |
| 15 | +<script id="expected0" type="plain/text"> |
| 16 | + autoescape=select_autoescape() |
| 17 | +</script> |
| 18 | +<script id="expected1" type="plain/text"> |
| 19 | + <h1>Hello {{ person }}!</h1> |
| 20 | +</script> |
| 21 | +<script id="expected2" type="plain/text"> |
| 22 | + result = Markup('Original name=') + name |
| 23 | +</script> |
| 24 | + |
| 25 | +<!-- Full pattern of correct answer --> |
| 26 | +<!-- TODO --> |
| 27 | +<script id="correct0" type="plain/text"> |
| 28 | +\s* autoescape = select_autoescape \( \) \s* |
| 29 | +</script> |
| 30 | +<script id="correct1" type="plain/text"> |
| 31 | +\s* < h1 >Hello\x20{{ person }}!< /h1 > \s* |
| 32 | +</script> |
| 33 | +<script id="correct2" type="plain/text"> |
| 34 | +\s* result = Markup \( ('Original name='|"Original name=") \) \+ name \s* |
| 35 | +</script> |
| 36 | + |
| 37 | +<script id="info" type="application/yaml"> |
| 38 | +--- |
| 39 | +hints: |
| 40 | +- absent: |- |
| 41 | + autoescape |
| 42 | + text: Add an `autoescape=` parameter. |
| 43 | +- present: |- |
| 44 | + autoescape [^:\x20] |
| 45 | + text: The name `autoescape` needs to be followed by `=`. |
| 46 | +- present: |- |
| 47 | + (Autoescape|AUTOESCAPE) |
| 48 | + text: The name `autoescape` must be in all lowercase. |
| 49 | +- present: |- |
| 50 | + ([Aa]uto_[Ee]scape|AUTO_ESCAPE) |
| 51 | + text: Use `autoescape` in all lowercase with no underscores. |
| 52 | +- present: |- |
| 53 | + \| safe |
| 54 | + index: 1 |
| 55 | + text: The text `| safe` indicates that this text is trusted and should |
| 56 | + not be escaped further. However, in context this data could be provided |
| 57 | + from an attacker and is NOT safe. Remove the marking. |
| 58 | +- present: |- |
| 59 | + \| |
| 60 | + index: 1 |
| 61 | + text: The `|` is used to separate the computed value from the safe marking, |
| 62 | + but we will not use that marking. Remove the vertical bar. |
| 63 | +- present: |- |
| 64 | + Markup \(.*\+.*\) |
| 65 | + index: 2 |
| 66 | + text: Having a concatenation (+) *inside* the call to Markup |
| 67 | + is a vulnerability. |
| 68 | + The call to Markup presumes we are passing text that is *not* supposed |
| 69 | + be escaped. If it is supposed to be escaped, it should be concatenated |
| 70 | + outside the initial construction of the Markup object. |
| 71 | +- absent: |- |
| 72 | + \+ |
| 73 | + index: 2 |
| 74 | + text: Our expected answer includes concatentation using `+`. |
| 75 | + We expect something like `Markup('Original name='` followed by `+` |
| 76 | + followed by the variable containing the data that needs to be escaped. |
| 77 | +# debug: true |
| 78 | +</script> |
| 79 | +</head> |
| 80 | +<body> |
| 81 | +<!-- For GitHub Pages formatting: --> |
| 82 | +<div class="container-lg px-3 my-5 markdown-body"> |
| 83 | +<h1>Lab Exercise xss</h1> |
| 84 | +<p> |
| 85 | +This is a lab exercise on developing secure software. |
| 86 | +For more information, see the <a href="introduction.html" target="_blank">introduction to |
| 87 | +the labs</a>. |
| 88 | + |
| 89 | +<p> |
| 90 | +<h2>Task</h2> |
| 91 | +<p> |
| 92 | +<b>Please practice countering Cross-Site Scripting (XSS) with Flask and the templating engine Jinja2.</b> |
| 93 | + |
| 94 | +<p> |
| 95 | +<h2>Background</h2> |
| 96 | +<p> |
| 97 | +In this exercise, we'll implement |
| 98 | +mechanisms to broadly counter Cross-Site Scripting (XSS) attacks, |
| 99 | +as |
| 100 | +<a href="https://github.com/ossf/secure-sw-dev-fundamentals/blob/main/secure_software_development_fundamentals.md#countering-cross-site-scripting-xss" |
| 101 | +>described in our course</a>. |
| 102 | +We noted that "The standard way to counter XSS is |
| 103 | +to escape all output that might |
| 104 | +be from an attacker and is not specifically approved. ... In most |
| 105 | +cases, the best solution for XSS is to choose a framework or library |
| 106 | +that automatically escapes HTML output for you." |
| 107 | +That is, we want to use a system that automatically escapes |
| 108 | +characters like "<" by converting them into "&lt;" |
| 109 | +unless we specifically note otherwise. |
| 110 | +That way, those special characters are rendered harmless. |
| 111 | +<p> |
| 112 | +In <i>theory</i> you could call an escape routine every time you |
| 113 | +make a call to generate an output. |
| 114 | +In <i>practice</i> this approach is insecure, |
| 115 | +because it's too easy to accidentally forget to call the escape routine. |
| 116 | +It's instead safer to use mechanisms which escape <i>by default</i>. |
| 117 | +<p> |
| 118 | +<a href="https://pypi.org/project/Flask/" |
| 119 | +>Flask</a> is a lightweight server-side web application framework |
| 120 | +for the Python programming language. |
| 121 | +Programs using Flask often use the |
| 122 | +<a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#rendering-templates">Jinja2 template library</a>. |
| 123 | +<a href="https://jinja.palletsprojects.com/en/3.1.x/api/#autoescaping" |
| 124 | +>Jinja2 has a mechanism for automatically escaping HTML</a>. |
| 125 | +<p> |
| 126 | +Jinja2's version 3.1.x series does <i>not</i> |
| 127 | +enable this "autoescape" mode by default. |
| 128 | +This may change in a future version of Jinja, but even so, |
| 129 | +this serves as a great example. |
| 130 | +In short, sometimes libraries must be specially configured to be |
| 131 | +less dangerous to use. |
| 132 | +This isn't ideal, but it can still be used. |
| 133 | +You simply need to ensure that you correctly |
| 134 | +configure the library to be used securely. |
| 135 | +<p> |
| 136 | +It turns out that |
| 137 | +<a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#rendering-templates" |
| 138 | +>Flask by default configures Jinja2 to automatically escape of HTML</a>. |
| 139 | +So as far |
| 140 | +as users of <i>Flask</i> are concerned, the Jinja templating system <i>does</i> |
| 141 | +automatically escape HTML by default. |
| 142 | + |
| 143 | +<p> |
| 144 | +<h2>Task Information</h2> |
| 145 | +<p> |
| 146 | +Please change the code below to counter XSS vulnerabilities. |
| 147 | +Use the “hint” and “give up” buttons if necessary. |
| 148 | +This is a multi-part task; the specific task instructions are interspersed |
| 149 | +below. |
| 150 | + |
| 151 | +<p> |
| 152 | +<h2>Interactive Lab (<span id="grade"></span>)</h2> |
| 153 | + |
| 154 | +<h3>Part 1</h3> |
| 155 | +<p> |
| 156 | +In this part, you are <i>not</i> using Flask. |
| 157 | +You're instead using Python and directly using the Jinja2 templating engine. |
| 158 | +The version of Jinja2 we're considering |
| 159 | +<a href="https://jinja.palletsprojects.com/en/3.1.x/api/#basics" |
| 160 | +>does <i>not</i> automatically escape unless it's configured to do so</a>. |
| 161 | +Modify the Jinja2 configuration to automatically escape HTML, by passing |
| 162 | +the field named <tt>autoescape</tt> with |
| 163 | +<tt>select_autoescape()</tt> as its value. |
| 164 | + |
| 165 | +<!-- |
| 166 | +You can use this an example for new labs. |
| 167 | +For multi-line inputs, instead of <input id="attempt0" type="text" ...>, use |
| 168 | +<textarea id="attempt" rows="2" cols="65">...</textarea> |
| 169 | +--> |
| 170 | +<form id="lab1"><pre><code |
| 171 | +>from jinja2 import Environment, PackageLoader, select_autoescape |
| 172 | +env = Environment( |
| 173 | + loader=PackageLoader("yourapp"), |
| 174 | +<input id="attempt0" type="text" size="60" spellcheck="false" value=""> |
| 175 | +) |
| 176 | +</code></pre> |
| 177 | +<button type="button" class="hintButton">Hint</button> |
| 178 | +<button type="button" class="resetButton">Reset</button> |
| 179 | +<button type="button" class="giveUpButton">Give up</button> |
| 180 | +</form> |
| 181 | + |
| 182 | +<h3>Part 2</h3> |
| 183 | +<p> |
| 184 | +In this part |
| 185 | +we <i>are</i> using Flask. Flask configures Jinja2 to automatically |
| 186 | +escape HTML. The code below |
| 187 | +(based on the |
| 188 | +<a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#rendering-templates" |
| 189 | +>Flask quickstart</a>) |
| 190 | +uses Flask's template rendering system, which |
| 191 | +in turn calls Jinja2 to render the result: |
| 192 | +</p> |
| 193 | + |
| 194 | +<pre><code |
| 195 | +>from flask import render_template |
| 196 | +@app.route('/hello/') |
| 197 | +@app.route('/hello/<name>') |
| 198 | +def hello(name=None): |
| 199 | + return render_template('hello.html', person=name)</code></pre> |
| 200 | + |
| 201 | +<p> |
| 202 | +The sample code above uses the template <tt>hello.html</tt>, which is |
| 203 | +shown below. |
| 204 | +Note that values to be substituted in the template |
| 205 | +are surrounded by <tt>{{ ... }}</tt>. |
| 206 | +<p> |
| 207 | +Unfortunately, this template below has a vulnerability. |
| 208 | +Its "| safe" marking tells the templating system that the data is |
| 209 | +safe and shouldn't be escaped. |
| 210 | +However, when the data <i>should</i> be escaped |
| 211 | +(as is often the case), this would lead to a vulnerability. |
| 212 | +For example, this would often lead to a vulnerability |
| 213 | +if an attacker can slip characters like "<" into a name, |
| 214 | +Please fix this vulnerability. |
| 215 | + |
| 216 | +<!-- |
| 217 | +You can use this an example for new labs. |
| 218 | +For multi-line inputs, instead of <input id="attempt0" type="text" ...>, use |
| 219 | +<textarea id="attempt" rows="2" cols="65">...</textarea> |
| 220 | +--> |
| 221 | +<form id="lab2"> |
| 222 | +<pre><code |
| 223 | +><!doctype html> |
| 224 | +<title>Hello from Flask</title> |
| 225 | +{% if person %} |
| 226 | +<input id="attempt1" type="text" size="50" spellcheck="false" |
| 227 | + value=" <h1>Hello {{ person | safe }}!</h1>"> |
| 228 | +{% endif %} |
| 229 | +</code></pre> |
| 230 | +<button type="button" class="hintButton">Hint</button> |
| 231 | +<button type="button" class="resetButton">Reset</button> |
| 232 | +<button type="button" class="giveUpButton">Give up</button> |
| 233 | +</form> |
| 234 | + |
| 235 | + |
| 236 | +<h3>Part 3</h3> |
| 237 | +<p> |
| 238 | +In this part we continue to use Flask. |
| 239 | +However, |
| 240 | +sometimes you need more sophisticated control over what is and is not escaped. |
| 241 | +Most web application frameworks have a type or class that records |
| 242 | +HTML values, and lets you specify what is to be escaped or not. |
| 243 | +In Flask this typically done with its |
| 244 | +<a href="https://flask.palletsprojects.com/en/3.0.x/templating/#controlling-autoescaping">Markup</a> class. |
| 245 | +<p> |
| 246 | +A instance of a <tt>Markup</tt> class is created by calling |
| 247 | +<tt>Markup</tt>. |
| 248 | +Whatever string is passed during its original construction |
| 249 | +is assumed to be safe and is <i>not</i> escaped. |
| 250 | +You can concatenate a normal string to a Markup value, but those additions |
| 251 | +<i>will</i> be escaped. |
| 252 | +<a href="https://tedboy.github.io/flask/generated/generated/flask.Markup.html" |
| 253 | +>For example</a>, computing |
| 254 | +<tt>Markup("<em>Hello</em> ") + "<foo>"</tt> |
| 255 | +produces a Markup instance containing the Unicode string value |
| 256 | +<tt>'<em>Hello</em> &lt;foo&gt;'</tt> - |
| 257 | +note how the first part isn't escaped but the latter part <i>is</i> escaped. |
| 258 | +Since every concatenation will be escaped by default, the default is |
| 259 | +the safe (escaping) operation. |
| 260 | +The code also clearly indicates what is considered safe and what is not. |
| 261 | +The Markup class supports many other methods not described here |
| 262 | +to simplify control over what is escaped. |
| 263 | +The templating system will directly include an instance of |
| 264 | +Markup without further escapes, because |
| 265 | +the Markup instance has already escaped whatever is supposed to be escaped. |
| 266 | +<p> |
| 267 | +The Python code below tries to use <tt>Markup</tt> to include a <tt>name</tt>. |
| 268 | +However, it's incorrect and insecure. |
| 269 | +The problem is that <tt>name</tt> is an untrusted value and |
| 270 | +it shouldn't be passed directly to <tt>Markup</tt> as part of its trusted value. |
| 271 | +Modify the code below so that <tt>result</tt> will include the <i>escaped</i> |
| 272 | +version of <tt>name</tt>. Note that in Python <tt>+</tt> is the |
| 273 | +string concatenation operation. |
| 274 | + |
| 275 | +<!-- |
| 276 | +You can use this an example for new labs. |
| 277 | +For multi-line inputs, instead of <input id="attempt0" type="text" ...>, use |
| 278 | +<textarea id="attempt" rows="2" cols="65">...</textarea> |
| 279 | +--> |
| 280 | +<form id="lab3"> |
| 281 | +<pre><code |
| 282 | +><input id="attempt2" type="text" size="60" spellcheck="false" |
| 283 | + value=" result = Markup('Original name=' + name)"> |
| 284 | +</code></pre> |
| 285 | +<button type="button" class="hintButton">Hint</button> |
| 286 | +<button type="button" class="resetButton">Reset</button> |
| 287 | +<button type="button" class="giveUpButton">Give up</button> |
| 288 | +</form> |
| 289 | + |
| 290 | +<p> |
| 291 | +<i>This lab was developed by David A. Wheeler at |
| 292 | +<a href="https://www.linuxfoundation.org/" |
| 293 | +>The Linux Foundation</a>.</i> |
| 294 | +<br><br> |
| 295 | +<p id="correctStamp" class="small"> |
| 296 | +<textarea id="debugData" class="displayNone" rows="20" cols="65" readonly> |
| 297 | +</textarea> |
| 298 | +</form> |
| 299 | +</div><!-- End GitHub pages formatting --> |
| 300 | +</body> |
| 301 | +</html> |
0 commit comments