diff --git a/docs/labs/ja_xss.html b/docs/labs/ja_xss.html new file mode 100644 index 00000000..ed97781e --- /dev/null +++ b/docs/labs/ja_xss.html @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + +
+

ラボ演習 xss

+

+これはセキュアなソフトウェア開発に関するラボ演習です。 +ラボの詳細については、概要をご覧ください。 + +

+

タスク

+

+FlaskとテンプレートエンジンのJinja2を用いてクロスサイトスクリプティング(XSS)への対策を練習しましょう。 + +

+

背景

+

+この演習では、コースで述べたように(訳注: リンク先は英語版)、クロスサイトスクリプティング(XSS)攻撃に対する広範な対策となる仕組みを実装します。 +ここでは、「XSS対策の標準的な方法は、攻撃者のものである可能性があり、特に承認されていないすべての出力をエスケープすることです。(中略) ほとんどの場合、XSSに対する最善の解決策は、HTML出力を自動的にエスケープしてくれるフレームワークやライブラリーを選択することです。」と述べました。 +すなわち、エスケープしないように指定しない限り、"<"のような文字を"&lt;"に変換するように、自動的にエスケープしてくれるシステムを用いるのがよいということです。 +このようにして、特殊な文字が無害な形で出力されます。 +

+理論的には、出力を生成する際に、毎回エスケープルーチンを呼び出すこともできます。現実的にはこのアプローチは安全ではありません。 +いつかは開発者が誤って、出力を生成する際にエスケープルーチンを呼ぶことを忘れてしまうことがあるでしょう。 +エスケープをデフォルトで実行してくれる仕組みを用いるほうがはるかに安全です。 +

+FlaskはPythonプログラミング言語向けのサーバーサイドのWebアプリケーションの軽量フレームワークです。 +Flaskを用いるプログラムでは、Jinja2テンプレートエンジンもよく使われています。 +Jinja2は、HTMLを自動的にエスケープする仕組みを持っています。 +

+Jinja2のバージョン3.1.xシリーズは、この「自動エスケープ」モードをデフォルトでは有効にしていません。 +これは、Jinjaの将来のバージョンでは変わるかもしれませんが、いずれにしても、これは良い例といえるでしょう。 +つまり、ライブラリによっては、危険ではない使い方をするために特殊な設定を行わなければならないことがあるということです。 +これは理想的ではありませんが、そのようなライブラリであっても使うことはできます。 +ライブラリを安全に使うために、正しく設定することをしっかりと確かめる必要があるというだけです。 +

+Flaskはデフォルトで自動的にHTMLをエスケープするようにJinja2を設定します。. +そのため、Flaskのユーザーに関していえば、Jinjaテンプレートシステムは、デフォルトでHTMLを自動的にエスケープします。 + +

+

タスクの詳細

+

+下記のコードを変更してXSS脆弱性の対策を行ってください。必要に応じて「ヒント」や「諦める」のボタンを押してください。 +このタスクは複数のパートから構成されています。各パートの指示はそれぞれの部分に分かれて書かれています。 + +

+

演習 ()

+ +

パート1

+

+このパートでは、Flaskは使いません。 +その代わり、PythonとJinja2テンプレートエンジンを直接使います。 +今回対象とするバージョンのJinja2は、そのように設定しない限り、自動的にエスケープを行いません。 +Jinja2の設定を変更してHTMLを自動的にエスケープするようにしてください。これには、autoescapeというフィールドにselect_autoscape()を値として設定することで行います。 + + +

from jinja2 import Environment, PackageLoader, select_autoescape
+env = Environment(
+    loader=PackageLoader("yourapp"),
+
+)
+
+ + + +
+ +

パート2

+

+このパートでは、Flaskを使います。 +FlaskはJinja2をHTMLを自動的にエスケープするように設定します。 +下記のコード(Flask quickstartを元にしています)は、Jinja2を中で呼び出すFlaskのテンプレートレンダリングシステムを用いて、結果を出力しています。 +

+ +
from flask import render_template
+@app.route('/hello/')
+@app.route('/hello/')
+def hello(name=None):
+    return render_template('hello.html', person=name)
+ +

+上のサンプルコードは、下記に示すhello.htmlというテンプレートを用いています。 +テンプレートで{{ ... }}で囲まれている部分には、値が代入されることに注意してください。 +

+残念なことに、下記のテンプレートには脆弱性があります。 +"| safe"というマークは、テンプレートシステムに対して、そのデータが安全でありエスケープするべきではないと指示するものです。 +しかし、上記のコードで示した通り、人物の名前(name)は、信頼できないユーザーから得たものです。 +したがって、(他のほとんどのデータと同様に)名前は安全ではありません。 +このままでは、攻撃者は"<"といった文字を名前に忍び込ませて、他社への攻撃の手段として使うことが可能です。 +この脆弱性を修正してください。 + + +

+
<!doctype html>
+<title>Hello from Flask</title>
+{% if person %}
+
+{% endif %}
+
+ + + +
+ + +

パート3

+

+このパートでも、Flaskを使い続けます。 +しかし、何をエスケープして何をエスケープしないかについて、より洗練されたコントロールが必要な場合があります。 +ほとんどのWebアプリケーションフレームワークには、HTMLの値を記録する型やクラスがあり、それによってどれをエスケープするかしないかを指定することができます。 +通常、FlaskではMarkupクラスを用います。 +

+Markupクラスのインスタンスは、Markupを呼ぶことによって作られます。 +最初にコンストラクタで与えられた文字列は安全であるとみなされ、エスケープされません。 +Markupクラスの値に対して通常の文字列を連結することができますが、その追加された部分はエスケープされます。 +例えばMarkup("<em>Hello</em> ") + "<foo>"という計算では、 +'<em>Hello</em> &lt;foo&gt;'というUnicode文字列をもつMarkupインスタンスが作成されます。 +このように、最初の部分はエスケープされておらず、後の部分はエスケープされていることに注意してください。 +Markupインスタンスを空文字列から作成することもでき、その場合、連結した文字列は全てエスケープされます。 +文字列のMarkupインスタンスに対する連結はデフォルトでエスケープされるため、デフォルトが安全な(エスケープ)操作になります。 +コードにおいても、何が安全で何が安全でないかを明確に示されることになります。 +ここでは説明しませんが、Markupクラスは、何をスケープするかのコントロールを単純化するメソッドを他にも持っています。 +テンプレートシステムは、Markupインスタンスをさらなるエスケープなしで直接使います。これは、エスケープすべきものは既にMarkupインスタンスがエスケープしているからです。 +

+下記のPythonコードは、nameを含む形でMarkupを使おうとしています。 +しかし、これは間違いで安全ではありません。 +問題は、nameが信頼されていない値であり、Markupに対して、信頼された値の一部として直接渡すべきではないことです。 +下記のコードを変更して、resultには、エスケープされたnameが入るように変更してください。 +Pythonにおいて+は文字列連結の演算であることに注意してください。 + + +

+

+
+ + + +
+ +

+このラボは、Linux FoundationのDavid A. Wheelerによって開発されました。 +

+

+ + +

+ + diff --git a/docs/labs/xss.js b/docs/labs/xss.js index 8cda6d01..87150ce3 100644 --- a/docs/labs/xss.js +++ b/docs/labs/xss.js @@ -7,6 +7,7 @@ info = { present: "(Autoescape|AUTOESCAPE)", text: "The name `autoescape` must be in all lowercase.", + text_ja: "`autoescape`という名前は全て小文字でなければなりません。", examples: [ [ "Autoescape" ], ], @@ -14,6 +15,7 @@ info = { present: "([Aa]uto_[Ee]scape|AUTO_ESCAPE)", text: "Use `autoescape` in all lowercase with no underscores.", + text_ja: "`autoescape`は、アンダースコアなしで全て小文字で書いてください。", examples: [ [ "auto_escape" ], ], @@ -21,6 +23,7 @@ info = { absent: "autoescape", text: "Add an `autoescape=` parameter.", + text_ja: "`autoescape=`というパラメータを追加してください。", examples: [ [ "" ], ], @@ -29,6 +32,7 @@ info = present: 'autoescape', absent: String.raw`autoescape\x20*=`, text: "The name `autoescape` needs to be followed by `=`.", + text_ja: "`=`が`autoescape`の後に必要です。", examples: [ [ "autoescape" ], ], @@ -37,6 +41,7 @@ info = present: String.raw`\| safe`, index: 1, text: "The text `| safe` indicates that this text is trusted and should not be escaped further. However, in context this data could be provided from an attacker and is NOT safe. Remove the marking.", + text_ja: "`| safe`は、このテキストが信頼できエスケープするべきではないことを表します。しかし、ここではこのデータは攻撃者によって与えられる可能性があり安全ではありません。このマークを消してください。", examples: [ [ null, "

Hello {{ person | safe }}!

" ], ], @@ -45,6 +50,7 @@ info = present: String.raw`\|`, index: 1, text: "The `|` is used to separate the computed value from the safe marking, but we will not use that marking. Remove the vertical bar.", + text_ja: "`|`は、計算される値とsafeのマークを区切るために使われていましたが、ここでは必要ありません。縦棒(|)を消してください。", examples: [ [ null, "

Hello {{ person | }}!

" ], ], @@ -53,6 +59,7 @@ info = present: String.raw`Markup \(.*\+.*\)`, index: 2, text: "Having a concatenation (+) *inside* the call to Markup is a vulnerability. The call to Markup presumes we are passing text that is *not* supposed be escaped. If it is supposed to be escaped, it should be concatenated outside the initial construction of the Markup object.", + text_ja: "Markupの呼び出しの*中に*連結(+)があることが脆弱性です。Markupの呼び出しは、渡したテキストがエスケープされる*べきでない*とみなします。エスケープされるべきものであれば、Markupオブジェクトの初期コントラクタの外で連結しなければなりません。", examples: [ [ null, null, " result = Markup('Original name=' + name)" ], ], @@ -61,6 +68,7 @@ info = absent: String.raw`\+`, index: 2, text: "Our expected answer includes concatentation using `+`. We expect something like `Markup('Original name='` followed by `+` followed by the variable containing the data that needs to be escaped.", + text_ja: "想定解は`+`を用いた連結です。`Markup('Original name=')`に続けて`+`、さらにエスケープされるべきデータが入っている変数が続く、というものを想定しています。", examples: [ [ null, null, ' result = Markup(f"Original name={name}' ], ],