はじめに
DjangoCongress JP 2023、1日ありがとうございました! nikkieです。
トークした「Djangoアプリに作り込んで学ぶ脆弱性(SQLインジェクションとXSS篇)」について延長戦コンテンツをお届けします
目次
- はじめに
- 目次
- トーク「Djangoアプリに作り込んで学ぶ脆弱性(SQLインジェクションとXSS篇)」
- nikkieの勘違い:デモで見せたcookieはDjangoアプリによるものではなかった
- Djangoのセッションのデフォルト設定
- XSSでcookieを他サーバに送信するために緩めた設定(非推奨)
- 終わりに
トーク「Djangoアプリに作り込んで学ぶ脆弱性(SQLインジェクションとXSS篇)」
https://djangocongress.jp/#talk-7
14:55〜「Djangoアプリに作り込んで学ぶ脆弱性(SQLインジェクションとXSS篇)」お話ししますhttps://t.co/seN8tMpn8h #djangocongress
— nikkie にっきー (@ftnext) 2023年10月7日
この発表の時間だけDjango製のやられアプリに一緒に悪いことしましょう!
XSS(持続型XSS)の例として、cookieを他サーバに送信するデモをしました。
JavaScriptで以下を実装しています
質疑の中で「document.cookie
でcookieにアクセス」について、DjangoではデフォルトでJavaScriptからcookieにアクセスできない設定なので、追加の設定をする必要があるというご指摘と理解しました。
XSSのcookieの件、質問いただいて私が誤解していたことに気づきました。ありがとうございます #djangocongress
— nikkie にっきー (@ftnext) 2023年10月7日
今回のアプリは徳丸さんのBad Todoオマージュで、徳丸さんアプリを127.0.0.1で動かした際のcookieが残っていたのを私が自作やられサイトのcookieと勘違いしてました。トーク延長戦はじまた
nikkieの勘違い:デモで見せたcookieはDjangoアプリによるものではなかった
ここは完全に勘違いしていました。申し訳ありません。
今回の発表のアプリは、徳丸さんによるBad Todoのオマージュです。
Bad Todoを127.0.0.1で動かしていたのですが、その中で127.0.0.1のcookieにTODOSESSID
が設定されたと思われます1(csrf_token
も同様かも)。
つまり、デモで見せたcookieは、徳丸さんによるBad Todoのcookieだったと分かりました(Djangoアプリでなかった)。
Djangoのセッションのデフォルト設定
今回はstartproject
したアプリですので、セッションが有効になっています。
ref: https://docs.djangoproject.com/ja/4.2/topics/http/sessions/#enabling-sessions
セッションエンジンとしては、デフォルト値の'django.contrib.sessions.backends.db'
です。
ref: https://docs.djangoproject.com/ja/4.2/ref/settings/#session-engine
つまり、データベースを使ったセッションであって、cookieを使ったセッションではありませんでした。
この設定は、cookieを送信するXSSの例としては不適切だったと分かりました。
発表内容に正確でない点があり、申し訳ありません。
(今回のデモの攻撃が成り立たないということで、セキュアではあります)
XSSでcookieを他サーバに送信するために緩めた設定(非推奨)
cookieを送信するXSSの例に沿った設定をします(=セキュアでない状態にします)。
プロジェクトのsettings.pyに以下を追加しました
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" SESSION_COOKIE_HTTPONLY = False SESSION_COOKIE_NAME = "TODOSESSID"
SESSION_ENGINE
をデフォルト値からクッキーを使ったセッションに変更します。
「クッキーを使ったセッション」には
保存されたデータに JavaScript からアクセスできないように、SESSION_COOKIE_HTTPONLY を True に設定しておくことをおすすめします。
とあります。
SESSION_COOKIE_HTTPONLY
のデフォルト値はTrue
なので、デフォルトではcookieに保存されたデータにJavaScriptからアクセスできません2。
https://docs.djangoproject.com/ja/4.2/ref/settings/#session-cookie-httponly
脆弱性を作り込んでデモをするという目的のために、False
に緩めます。
SESSION_COOKIE_NAME
はcookieのキーを徳丸さんのBad Todoと合わせるために指定しました(オマージュなので!)
https://docs.djangoproject.com/ja/4.2/ref/settings/#session-cookie-name
これでトークと同様にデモをし、TODOSESSIDを含むcookieがモックサーバに送られることを確認しています。
また、通常のブラウザとシークレットウィンドウとで別々のユーザでログインし、TODOSESSIDが変わることも確認しました。
終わりに
Djangoのセッションのcookieについて、トークをきっかけに自分の誤解に気づきました。
「トークのデモにはsettings.pyの設定も必要」と追加して訂正いたします。
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" SESSION_COOKIE_HTTPONLY = False SESSION_COOKIE_NAME = "TODOSESSID"
トークする前に気付ければとてもよかったわけですが、発表へのコメントを機に甘かった理解を更新できたので、コメントしてくださった方(うめさん)には大感謝です。
また、本発表を聞きに来て(一緒に悪いことして)くださった皆さま、ありがとうございました!
- https://github.com/ockeghem/badtodo/blob/71bc5466f9da4e81d9f14bf327ba5e4fe629cdb9/www/html/todo/common.php#L2↩
-
モックサーバには
csrftoken
しか送られてきません↩