Skip to content

Commit 4f4be37

Browse files
Add lab xss.html (#645)
* Add lab xss.html This adds a lab to practice countering cross-site scripting (XSS) vulnerabilities. I decided to use Python, Flask, and Jinja2, as they're common and easy to exaplain. Also, Jinja2 and Flask have a quirk - Jinja2 doesn't escape by default, but when brought in via Flask, Flask enables it. That gives us an easy excuse to discuss how to configure templating systems as necessary. I ended up making this a multi-part lab. That seemed like the best way to illustrate some important concepts when talking about how to escape values at scale. Signed-off-by: David A. Wheeler <[email protected]> * Modify lab README to list xss Signed-off-by: David A. Wheeler <[email protected]> --------- Signed-off-by: David A. Wheeler <[email protected]>
1 parent bf43c60 commit 4f4be37

File tree

2 files changed

+302
-1
lines changed

2 files changed

+302
-1
lines changed

docs/labs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ work on.
113113
* Countering Denial-of-Service (DoS) Attacks - PLANNED-2 UNASSIGNED
114114
* Sending Output
115115
* [Introduction to Sending Output](https://github.com/ossf/secure-sw-dev-fundamentals/blob/main/secure_software_development_fundamentals.md#introduction-to-sending-output) - PLANNED-2 UNASSIGNED
116-
* [Countering Cross-Site Scripting (XSS)](https://github.com/ossf/secure-sw-dev-fundamentals/blob/main/secure_software_development_fundamentals.md#countering-cross-site-scripting-xss) - PLANNED-1 UNASSIGNED
116+
* [Countering Cross-Site Scripting (XSS)](https://github.com/ossf/secure-sw-dev-fundamentals/blob/main/secure_software_development_fundamentals.md#countering-cross-site-scripting-xss) - DONE-1 (David A. Wheeler) [xss](xss.html)
117117
* Content Security Policy (CSP) - DONE-0 [csp1](csp1.html)
118118
* Other HTTP Hardening Headers - (probably continue csp1) PLANNED-2 UNASSIGNED
119119
* [Cookies Cookies & Login Sessions Login Sessions](https://github.com/ossf/secure-sw-dev-fundamentals/blob/main/secure_software_development_fundamentals.md#cookies--login-sessions) - PLANNED-2 (Dhananjay Arunesh via Vincent Danen)

docs/labs/xss.html

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
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 "&lt;" by converting them into "&amp;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 "&lt;" 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+
>&lt;!doctype html&gt;
224+
&lt;title&gt;Hello from Flask&lt;/title&gt;
225+
{% if person %}
226+
<input id="attempt1" type="text" size="50" spellcheck="false"
227+
value=" &lt;h1&gt;Hello {{ person | safe }}!&lt;/h1&gt;">
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("&lt;em&gt;Hello&lt;/em&gt; ") + "&lt;foo&gt;"</tt>
255+
produces a Markup instance containing the Unicode string value
256+
<tt>'&lt;em&gt;Hello&lt;/em&gt; &amp;lt;foo&amp;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

Comments
 (0)