|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="ja"> |
| 3 | +<head> |
| 4 | +<meta charset="utf-8"> |
| 5 | +<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| 6 | +<meta name="viewport" content="width=device-width, initial-scale=1"> |
| 7 | +<link rel="stylesheet" href="https://best.openssf.org/assets/css/style.css"> |
| 8 | +<link rel="stylesheet" href="checker.css"> |
| 9 | +<script src="checker.js"></script> |
| 10 | +<script src="xss.js"></script> |
| 11 | +<link rel="license" href="https://creativecommons.org/licenses/by/4.0/"> |
| 12 | + |
| 13 | +<!-- See create_labs.md for how to create your own lab! --> |
| 14 | + |
| 15 | +</head> |
| 16 | +<body> |
| 17 | +<!-- For GitHub Pages formatting: --> |
| 18 | +<div class="container-lg px-3 my-5 markdown-body"> |
| 19 | +<h1>ラボ演習 xss</h1> |
| 20 | +<p> |
| 21 | +これはセキュアなソフトウェア開発に関するラボ演習です。 |
| 22 | +ラボの詳細については、<a href="ja_introduction.html" target="_blank">概要</a>をご覧ください。 |
| 23 | + |
| 24 | +<p> |
| 25 | +<h2>タスク</h2> |
| 26 | +<p> |
| 27 | +<b>FlaskとテンプレートエンジンのJinja2を用いてクロスサイトスクリプティング(XSS)への対策を練習しましょう。</b> |
| 28 | + |
| 29 | +<p> |
| 30 | +<h2>背景</h2> |
| 31 | +<p> |
| 32 | +この演習では、<a href="https://github.com/ossf/secure-sw-dev-fundamentals/blob/main/secure_software_development_fundamentals.md#countering-cross-site-scripting-xss">コースで述べたように(訳注: リンク先は英語版)</a>、クロスサイトスクリプティング(XSS)攻撃に対する広範な対策となる仕組みを実装します。 |
| 33 | +ここでは、「XSS対策の標準的な方法は、攻撃者のものである可能性があり、特に承認されていないすべての出力をエスケープすることです。(中略) ほとんどの場合、XSSに対する最善の解決策は、HTML出力を自動的にエスケープしてくれるフレームワークやライブラリーを選択することです。」と述べました。 |
| 34 | +すなわち、エスケープしないように指定しない限り、"<"のような文字を"&lt;"に変換するように、自動的にエスケープしてくれるシステムを用いるのがよいということです。 |
| 35 | +このようにして、特殊な文字が無害な形で出力されます。 |
| 36 | +<p> |
| 37 | +<i>理論的には</i>、出力を生成する際に、毎回エスケープルーチンを呼び出すこともできます。<i>現実的には</i>このアプローチは安全ではありません。 |
| 38 | +いつかは開発者が誤って、出力を生成する際にエスケープルーチンを呼ぶことを忘れてしまうことがあるでしょう。 |
| 39 | +エスケープを<i>デフォルトで</i>実行してくれる仕組みを用いるほうがはるかに安全です。 |
| 40 | +<p> |
| 41 | +<a href="https://pypi.org/project/Flask/" |
| 42 | +>Flask</a>はPythonプログラミング言語向けのサーバーサイドのWebアプリケーションの軽量フレームワークです。 |
| 43 | +Flaskを用いるプログラムでは、<a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#rendering-templates">Jinja2テンプレートエンジン</a>もよく使われています。 |
| 44 | +<a href="https://jinja.palletsprojects.com/en/3.1.x/api/#autoescaping">Jinja2は、HTMLを自動的にエスケープする仕組みを持っています。</a> |
| 45 | +<p> |
| 46 | +Jinja2のバージョン3.1.xシリーズは、この「自動エスケープ」モードをデフォルトでは有効にして<i>いません</i>。 |
| 47 | +これは、Jinjaの将来のバージョンでは変わるかもしれませんが、いずれにしても、これは良い例といえるでしょう。 |
| 48 | +つまり、ライブラリによっては、危険ではない使い方をするために特殊な設定を行わなければならないことがあるということです。 |
| 49 | +これは理想的ではありませんが、そのようなライブラリであっても使うことはできます。 |
| 50 | +ライブラリを安全に使うために、<i>正しく</i>設定することをしっかりと確かめる必要があるというだけです。 |
| 51 | +<p> |
| 52 | +<a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#rendering-templates" |
| 53 | +>Flaskはデフォルトで自動的にHTMLをエスケープするようにJinja2を設定します。</a>. |
| 54 | +そのため、<i>Flask</i>のユーザーに関していえば、Jinjaテンプレートシステムは、デフォルトでHTMLを自動的にエスケープ<i>します</i>。 |
| 55 | + |
| 56 | +<p> |
| 57 | +<h2>タスクの詳細</h2> |
| 58 | +<p> |
| 59 | +下記のコードを変更してXSS脆弱性の対策を行ってください。必要に応じて「ヒント」や「諦める」のボタンを押してください。 |
| 60 | +このタスクは複数のパートから構成されています。各パートの指示はそれぞれの部分に分かれて書かれています。 |
| 61 | + |
| 62 | +<p> |
| 63 | +<h2>演習 (<span id="grade"></span>)</h2> |
| 64 | + |
| 65 | +<h3>パート1</h3> |
| 66 | +<p> |
| 67 | +このパートでは、Flaskは<i>使いません</i>。 |
| 68 | +その代わり、PythonとJinja2テンプレートエンジンを直接使います。 |
| 69 | +今回対象とするバージョンのJinja2は、<a href="https://jinja.palletsprojects.com/en/3.1.x/api/#basics">そのように設定しない限り、自動的にエスケープを行い<i>ません</i></a>。 |
| 70 | +Jinja2の設定を変更してHTMLを自動的にエスケープするようにしてください。これには、<tt>autoescape</tt>というフィールドに<tt>select_autoscape()</tt>を値として設定することで行います。 |
| 71 | + |
| 72 | +<!-- |
| 73 | +You can use this an example for new labs. |
| 74 | +For multi-line inputs, instead of <input id="attempt0" type="text" ...>, use |
| 75 | +<textarea id="attempt" rows="2" cols="65">...</textarea> |
| 76 | +--> |
| 77 | +<form id="lab1"><pre><code |
| 78 | +>from jinja2 import Environment, PackageLoader, select_autoescape |
| 79 | +env = Environment( |
| 80 | + loader=PackageLoader("yourapp"), |
| 81 | +<input id="attempt0" type="text" size="60" spellcheck="false" value=""> |
| 82 | +) |
| 83 | +</code></pre> |
| 84 | +<button type="button" class="hintButton">ヒント</button> |
| 85 | +<button type="button" class="resetButton">リセット</button> |
| 86 | +<button type="button" class="giveUpButton">諦める</button> |
| 87 | +</form> |
| 88 | + |
| 89 | +<h3>パート2</h3> |
| 90 | +<p> |
| 91 | +このパートでは、Flaskを<i>使います</i>。 |
| 92 | +FlaskはJinja2をHTMLを自動的にエスケープするように設定します。 |
| 93 | +下記のコード(<a href="https://flask.palletsprojects.com/en/3.0.x/quickstart/#rendering-templates" |
| 94 | +>Flask quickstart</a>を元にしています)は、Jinja2を中で呼び出すFlaskのテンプレートレンダリングシステムを用いて、結果を出力しています。 |
| 95 | +</p> |
| 96 | + |
| 97 | +<pre><code |
| 98 | +>from flask import render_template |
| 99 | +@app.route('/hello/') |
| 100 | +@app.route('/hello/<name>') |
| 101 | +def hello(name=None): |
| 102 | + return render_template('hello.html', person=name)</code></pre> |
| 103 | + |
| 104 | +<p> |
| 105 | +上のサンプルコードは、下記に示す<tt>hello.html</tt>というテンプレートを用いています。 |
| 106 | +テンプレートで<tt>{{ ... }}</tt>で囲まれている部分には、値が代入されることに注意してください。 |
| 107 | +<p> |
| 108 | +残念なことに、下記のテンプレートには脆弱性があります。 |
| 109 | +"| safe"というマークは、テンプレートシステムに対して、そのデータが安全でありエスケープするべきではないと指示するものです。 |
| 110 | +しかし、上記のコードで示した通り、人物の名前(name)は、信頼できないユーザーから得たものです。 |
| 111 | +したがって、(他のほとんどのデータと同様に)名前は安全では<i>ありません</i>。 |
| 112 | +このままでは、攻撃者は"<"といった文字を名前に忍び込ませて、他社への攻撃の手段として使うことが可能です。 |
| 113 | +この脆弱性を修正してください。 |
| 114 | + |
| 115 | +<!-- |
| 116 | +You can use this an example for new labs. |
| 117 | +For multi-line inputs, instead of <input id="attempt0" type="text" ...>, use |
| 118 | +<textarea id="attempt" rows="2" cols="65">...</textarea> |
| 119 | +--> |
| 120 | +<form id="lab2"> |
| 121 | +<pre><code |
| 122 | +><!doctype html> |
| 123 | +<title>Hello from Flask</title> |
| 124 | +{% if person %} |
| 125 | +<input id="attempt1" type="text" size="50" spellcheck="false" |
| 126 | + value=" <h1>Hello {{ person | safe }}!</h1>"> |
| 127 | +{% endif %} |
| 128 | +</code></pre> |
| 129 | +<button type="button" class="hintButton">ヒント</button> |
| 130 | +<button type="button" class="resetButton">リセット</button> |
| 131 | +<button type="button" class="giveUpButton">諦める</button> |
| 132 | +</form> |
| 133 | + |
| 134 | + |
| 135 | +<h3>パート3</h3> |
| 136 | +<p> |
| 137 | +このパートでも、Flaskを使い続けます。 |
| 138 | +しかし、何をエスケープして何をエスケープしないかについて、より洗練されたコントロールが必要な場合があります。 |
| 139 | +ほとんどのWebアプリケーションフレームワークには、HTMLの値を記録する型やクラスがあり、それによってどれをエスケープするかしないかを指定することができます。 |
| 140 | +通常、Flaskでは<a href="https://flask.palletsprojects.com/en/3.0.x/templating/#controlling-autoescaping">Markup</a>クラスを用います。 |
| 141 | +<p> |
| 142 | +<tt>Markup</tt>クラスのインスタンスは、<tt>Markup</tt>を呼ぶことによって作られます。 |
| 143 | +最初にコンストラクタで与えられた文字列は安全であるとみなされ、エスケープ<i>されません</i>。 |
| 144 | +Markupクラスの値に対して通常の文字列を連結することができますが、その追加された部分はエスケープ<i>されます</i>。 |
| 145 | +<a href="https://tedboy.github.io/flask/generated/generated/flask.Markup.html" |
| 146 | +>例えば</a>、<tt>Markup("<em>Hello</em> ") + "<foo>"</tt>という計算では、 |
| 147 | +<tt>'<em>Hello</em> &lt;foo&gt;'</tt>というUnicode文字列をもつMarkupインスタンスが作成されます。 |
| 148 | +このように、最初の部分はエスケープされておらず、後の部分はエスケープ<i>されている</i>ことに注意してください。 |
| 149 | +Markupインスタンスを空文字列から作成することもでき、その場合、連結した文字列は全てエスケープされます。 |
| 150 | +文字列のMarkupインスタンスに対する連結はデフォルトでエスケープされるため、デフォルトが安全な(エスケープ)操作になります。 |
| 151 | +コードにおいても、何が安全で何が安全でないかを明確に示されることになります。 |
| 152 | +ここでは説明しませんが、Markupクラスは、何をスケープするかのコントロールを単純化するメソッドを他にも持っています。 |
| 153 | +テンプレートシステムは、Markupインスタンスをさらなるエスケープなしで直接使います。これは、エスケープすべきものは既にMarkupインスタンスがエスケープしているからです。 |
| 154 | +<p> |
| 155 | +下記のPythonコードは、<tt>name</tt>を含む形で<tt>Markup</tt>を使おうとしています。 |
| 156 | +しかし、これは間違いで安全ではありません。 |
| 157 | +問題は、<tt>name</tt>が信頼されていない値であり、<tt>Markup</tt>に対して、信頼された値の一部として直接渡すべきではないことです。 |
| 158 | +下記のコードを変更して、<tt>result</tt>には、<i>エスケープされた</i><tt>name</tt>が入るように変更してください。 |
| 159 | +Pythonにおいて<tt>+</tt>は文字列連結の演算であることに注意してください。 |
| 160 | + |
| 161 | +<!-- |
| 162 | +You can use this an example for new labs. |
| 163 | +For multi-line inputs, instead of <input id="attempt0" type="text" ...>, use |
| 164 | +<textarea id="attempt" rows="2" cols="65">...</textarea> |
| 165 | +--> |
| 166 | +<form id="lab3"> |
| 167 | +<pre><code |
| 168 | +><input id="attempt2" type="text" size="60" spellcheck="false" |
| 169 | + value=" result = Markup('Original name=' + name)"> |
| 170 | +</code></pre> |
| 171 | +<button type="button" class="hintButton">ヒント</button> |
| 172 | +<button type="button" class="resetButton">リセット</button> |
| 173 | +<button type="button" class="giveUpButton">諦める</button> |
| 174 | +</form> |
| 175 | + |
| 176 | +<p> |
| 177 | +<i>このラボは、<a href="https://www.linuxfoundation.org/">Linux Foundation</a>のDavid A. Wheelerによって開発されました。</i> |
| 178 | +<br><br> |
| 179 | +<p id="correctStamp" class="small"> |
| 180 | +<textarea id="debugData" class="displayNone" rows="20" cols="65" readonly> |
| 181 | +</textarea> |
| 182 | +</form> |
| 183 | +</div><!-- End GitHub pages formatting --> |
| 184 | +</body> |
| 185 | +</html> |
0 commit comments