Django Rest Frameworkの限定的なXSSバグ

Django Rest Frameworkの限定的なXSSバグ
ソウル、ソンスドンの中でー

こんにちは。韓国でウェブハッキングを(特に offensive security )しているユンソクチャンともうします。最近、日本語を勉強していて、日本で仕事をしたいと思いますので、この文は日本語でも翻訳します。

x.com

じゃあー、よろしくお願いします。

3行要約

  • Django Rest Frameworkで特定の状況においてXSS脆弱性が発生する可能性があることを探しました。
  • 実際には『セキュリティ脆弱性』ではなく、このような Potential Bug が発生する可能性がある程度の問題です。
  • 日本が好きな韓国人として、日本人ハッカーのみなさんと仲良くなりたいです。

Intro

今年のはじめ、Django Rest Frameworkのソースコードを分析し、XSS攻撃に脆弱なコードを探しました。このイッシュは Django Security チームには報告したので、私の blog にこうやって報告書の感じで整理してみます。

みなさんは下のコードで XSSができると思いますか。それならば、なぜできたのだと思いますか。

(上のコードで、path() で指定された path ‘api/’にアクセスすれば、下のようなREST APIの例示の画面が見えます。)

このコードでXSS脆弱性を見つける方は少ないと思います。なぜなら、rest_framework.response.Responseクラスを使用する際、XSSが発生することを説明している文書がないためです。

それじゃあ、この文では rest_framework.response.Response クラスでヘッダーを直接に指定することがなぜ危ないのかを説明します。

Django Rest Framework

Django Rest Framework (DRF) とは、Djangoで作られたweb serverにてHTTPを基盤としたREST APIを簡単に具現できるよう手伝う3rd partyライブラリです。最近は Django Ninjaというライブラリが人気ですが、何年前までも Djangoを基盤にREST APIを具現するのためには、DRFライブラリくらいしかありませんでした。私がこの文を書いている現在、Githubで27.6kものスターを得るほど、広く使われてるライブラリだと言えます。

GitHub - encode/django-rest-framework: Web APIs for Django. 🎸
Web APIs for Django. 🎸. Contribute to encode/django-rest-framework development by creating an account on GitHub.

そして、Django Security Teamを通して security reportをもらっているから、実際には公式のライブラリだと思います。

DjangoはどうやってXSSを防ぐのか

DjangoはJinja基盤のTemplate機能でHTMLコードを生成します。この際、Djangoから作れるHTMLコードには使用者が入力した値が入られますが、DjangoのTemplate機能はどうやってXSSを防御するのかしってみます。

上のコードはDjangoでTemplateを生成するための`render()`というメソッドを通して、使用者の入力値をtemplateへ渡す例示です。

test.html

上のpythonコードで使用されるtemplateであるtest.htmlです。test.htmlファイルは、viewメソッドからnameの値をもらって {{name}} 文字列を変えます。

結果

実際に以前に作成されたviewにHTTP要請を送れば、HTML entity エンコーディングを通して、特殊文字が変わったことが見られます。それでは、Django Template機能を使用するには、特殊文字のエンコーディングの使用したい時は、Djangoのtemplate filterである`safe`を使います。

safeフィルターはテンプレートの中で上みたいなコードで使えます。

今回は、特殊文字がエンコーディングされないでそのままに出力されました。

safe フィルターのソースコード分析

それではsafeというtemplate filterの作動方法を簡単に分析してみます。

safe template filterでは、使用者からもらう値を持って内部的にmark_safe()というメソッドを実行する後、リターンします。このmark_safe()メソッドのなかではSafeStringというクラスを通して、一般的なstr typeのデータを明示的にSafeStringクラスのオブジェクトに変換しています。

このSateStringクラスはDjango Templateの中で、「もう処理しまったから安全なデータ」だと判断されます。従って、SafeStringタイプのデータの特殊文字はもうHTML entityでエンコーディングされないです。


ではー

  1. safeというtemplate filterを使用したり
  2. mark_safe()というメソッドを使用して

使用者の入力が入っているデータを処理すれば、XSSが発生する可能性があると思えますね。🤔


Django Rest Frameworkのソースコード分析

今度はDRFライブラリのソースコードを分析して、APIViewクラスがどうやってHTTP Responseを作るのか知ってみます。

rest_framework/settings.py

rest_framework/settings.py

rest_framework/settings.pyを見れば、rest_framework.renderers.BrowsableAPIRendererというクラスがDRFのAPIViewクラスの基本レンダーラだとことを知れます。続いて、このBrowsableAPIRendererクラスを分析して見ます。

rest_framework/renderers.py

rest_framework/renderers.py

BrowsableAPIRendererクラスでは、DRFがrest_framework/api.htmlファイルの基盤でTemplateレンダーリングして、API Documentation画面をリターンすることが分かります。

rest_framework/api.html

rest_framework/api.html

前に使用されたapi.htmlファイルは、また同じディレクトリ内のbase.htmlファイルを使用します。

rest_framework/base.html

rest_framework/base.html

rest_framework/base.htmlファイルではresponse_headersという変数をbreak_long_headersというtemplate filterを通して、出力しています。最後にこのbreak_long_headersを分析してみます。

break_long_headers Template Filter

break_long_headers Template Filter

このtemplate filterではheaderという変数をもらっています。この変数には、HTTPの応答headersのkey、value中のvalueの値が入って来ます。ところでこの関数…さき見たmark_safe()メソッドを使用しています。そうして、mark_safe()に入って行く値であるheaderでは、使用者が入力した文字列が入っている可能性があります。

Exploitation

views.py

それでは、また初めのviews.pyのコードをもう一度分析してみます。

このコード…使用者からもらう値をSet-Cookieという応答ヘッダーに入れています。さきの分析した結果、このようなコードはXSSができますから、

http://localhost:8000/api/?foo=<img+src=x+onerror=prompt()>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
XSS成功!

おつかれさまでした!XSS成功です。

しかし…

しかし、このXSSは完璧なく脆弱性です。

まず、HTTP応答ヘッダーにユーザーの入力値が含まれる場合はほとんどありませんです。ユーザー入力が含まれるheaderは、Set-Cookie、Location、Content-Location程度でしましょう。

次に、もしResponse Headerにユーザーの入力値が含まれるとしても、RFC 3986に従って、その値は必ずURLエンコーディングされる必要があります。さらに、Set-Cookieヘッダーの場合、通常はResponseクラスのset_cookie()メソッドを通じて指定され、このメソッドにはURLエンコーディングのコードが含まれています。

したがって、標準的なセキュリティ規格を遵守してコーディングされていれば、絶対に発見されることのない脆弱性です。Djangoセキュリティチームもこの点を理由に、今回の問題を脆弱性として扱わないと回答しました。

結論

  • RFC規約に従ってセキュアコーディングをしましょう。
  • 基本機能を利用しましょう。
  • セキュリティホールとして認められず残念でした。

一週間かけて日本語の辞書を引きながら書いた文章ですが、どうでしょうか?面白く読んでいただけたなら、私のプロフィールも見ていただけると嬉しいです!

自己紹介
個人情報 * 名前:ユン ソクチャン * 生年月日:2001.02.20. * イーメール:ch4n3.yoon@gmail.com * CTFで活動したニックネーム:ch4n3 学校 * 韓国デジタルメディア高校学校 ハッキング防御課 卒業 (2017.03. ~ 2020.02.) Graduated Korea Digital Media High School, Dept. of Hacking Defense. * Majoring Computer Science, at KyungHee University, Korea. (2020.03. ~ )…