OWASP ZAPを使用したDjangoアプリの脆弱性診断
OWASP ZAPを使用して、自作したDjangoアプリに対して脆弱性診断を行います。
今回の脆弱性診断の対象とするのは、「動かして学ぶDjango開発入門」を参考にして作成したWebアプリです。(以後、このアプリを日記アプリとよびます)
注意
脆弱性診断を試す際には、自分が管理しているサイトやBadstoreのような脆弱性診断用のWebアプリを使用してください。外部のサイトに対して攻撃を行わないようにしてください。
また、脆弱性診断の結果、データベースやウェブサイトが破壊されてしまったり、不要なデータが大量に登録されてしまうことがあります。そのため、なるべく本番環境での診断は行わず、診断用の環境で診断を行うようにしてください。
この記事の目次
環境
- OS: Windows 11 Pro
- ブラウザ: Firefox
- 脆弱性診断ツール: OWASP ZAP
脆弱性診断ツールのOWASP ZAPは脆弱性診断を自動で行って、その結果をレポートとして出力します。
Firefoxはデフォルトの状態では特にセキュリティ機能を持ちません。そのため、脆弱性診断を行うのに便利です。
日記アプリの概要
今回の脆弱性診断の対象となる日記アプリについて簡単に説明します。
トップページ
URL: http://localhost:8000/
ログイン画面
メールアドレスとパスワードでログインをします
URL: http://localhost:8000/accounts/login/
サインアップ画面
新規のアカウントを登録する画面です。
URL: http://localhost:8000/accounts/signup/
パスワードリセット画面
URL: http://localhost:8000/accounts/password/reset/
管理者ログイン画面
URL: http://localhost:8000/admin
管理者ページへのログイン画面です。管理者アカウントのユーザーネームとパスワードを入力してログインします。
管理者用ページ
問い合わせ画面
URL: http://localhost:8000/inquiry/
日記一覧画面
URL: http://localhost:8000/diary-list/
作成した日記の一覧が表示されます。ログイン成功時には、こちらの画面にリダイレクトします。
日記作成画面
URL: http://localhost:8000/diary-create/
日記詳細画面
URL: http://localhost:8000/diary-detail/(日記のID)/
対応するIDを持つ日記に対して、詳細を表示するページです。
日記の編集、削除を選択できます。
日記編集画面
URL: http://localhost:8000/diary-update/(日記のid)/
対応するIDを持つ日記に対して、編集を行うページです。
事前準備
OWASP ZAPとFirefoxをインストールしてください。OWASP ZAPを使用するにはJAVAの実行環境が必要なので、JAVAの実行環境がインストールされていない場合はそちらも合わせてインストールしてください。
- OWASP ZAPのダウンロードリンク https://www.zaproxy.org/
- JAVAのダウンロードリンク https://www.java.com/ja/
- JAVA adoptim(Open JDKの一種)のダウンロードリンク https://adoptium.net/
Firefoxの設定
OWASP ZAPをプロキシとして設定します。
まず、FoxyProxy Standardという、ブラウザのプロキシを容易に変更できるアドオンをインストールしてください。
FoxyProxy Standardの「オプション」をクリックして、「プロキシー」を選択します。
左上の追加ボタンから、次の画像のようにOWASP ZAP の設定を行います。
ポート番号は他のアプリケーションと被らないように注意してください。
入力が終了したら、「保存」をクリックした後に、アドオンのFoxyProxy Standardをクリックしてください。新たに追加したOWASP ZAPがプロキシの候補として表示されています。
OWASP ZAPを使用する際は使用するFire FoxでプロキシをOWASP ZAPにする必要があることに注意してください。また、プロキシをOWASP ZAPに切り替える前にOWASP ZAPを起動していないと、通信を行うことができません。
また、今回はHTTPでの通信のみを行いますが、OWASP ZAPを経由してHTTPS通信を行う際には OWASP ZAPの証明書をFirefoxにインストールする必要があります。具体的な手順はこちらの記事が参考になります。
https://sqripts.com/2023/07/21/60002/
OWASP ZAPの設定
OWASP ZAPで日記アプリに対して脆弱性診断を行うための事前準備をします。
注意
OWASP ZAPをプロキシとして日記アプリにアクセスした際に、次のようなCSRFエラーが出てしまいました。
OWASP ZAPのHUDを次の手順でオフにしたことで解決しました。
- 「オプション」(歯車のアイコン)をクリック -> 「HUD」を選択 -> 一番上の「Enable when using the ZAP Desktop」をオフにする
OWASP ZAPのモードを「標準モード」から「プロテクトモード」に変更
モードの変更は画面の左上で行うことができます。
「プロテクトモード」では指定したコンテキスト(URLのまとまり)に対してのみ診断を行います。そのため、誤って他のサイトに対して攻撃をしてしまうことを防ぐことができます。
セッションIDを含むCookieの名前を登録
日記アプリでセッション管理を行っているセッションIDの名前は「sessionid」です。
オプション(画面上部の歯車のマーク)をクリックして、Httpセッションを選択します。
規定のセッショントークンに一覧に「sessionid」が含まれていない場合は、「追加」ボタンから新たに「sessionid」を追加します。
CSRF対策トークンの名前を登録
Djangoではタグの内部に{% csrf_token %}というテンプレートタグを含めることで次のように、name=csrfmiddlewaretokenのinputタグが自動的に生成されます。
次のように、name=csrfmiddlewaretokenのinputタグが自動的に生成されます。
<input type="hidden" name="csrfmiddlewaretoken" value="ME7FSE8J3xoGSQKVRFNLKMH5f7evJjARTk5oKg7m64lAOWab7umky4mFXQHP4gUq">
valueにはサーバー側で発行されたトークンが設定されています。
Djangoは、デフォルト(MiddlewareのCsrfViewMiddleWareが設定されている状態)では、送られてきたトークンがCookieに格納していた値と等しいか検証することで、CSRF対策をすることができます。
設定から「アンチCSRFトークン」を選択して、トークンの一覧にcsrfmiddlewaretokenが含まれていることを確認します。含まれていない場合には新たに追加する必要がありますが、今回は最初からトークンの一覧に含まれているので、そのままで大丈夫です。
手動クロールによる診断対象の記録
手動クロールによって、日記アプリのサイト構成をOWASP ZAPに記録させます。
診断対象となるすべてのページをFirefoxを使って一通りアクセスしてください。
自動クロールによる診断対象の記録
検出漏れを防ぐために、必要に応じてOWASP ZAPの自動クロール機能である「スパイダー」を使用します。
ツリーのサイトから診断対象のURLを右クリックしてください。「攻撃」->「スパイダー」を選択すると「スパイダー」ウィンドウが開きます。「スパイダー」ウィンドウの「コンテキスト」、「ユーザー」には、対象となるコンテキスト、あらかじめ設定したユーザーをそれぞれ指定してください。
「スキャンを開始」をクリックすると、自動クロールが開始します
コンテキストの作成
画面左側のサイトの一覧から、追加したいURL(今回はhttp://localhost8000)を選択して右クリックをします。右クリックで開いたメニューから、「コンテキストに含める」を選択して、新規コンテキストをクリックします。
ファイルからセッションのプロパティを選択して、「セッションプロパティ」ウィンドウを開きます。「セッションプロパティ」のコンテキスト欄に、新たにhttp://localhost8000が登録されているはずです。
「セッションプロパティ」ウィンドウ内の対象コンテキストの「コンテキストに含める」をクリックすると、Regex欄にhttp://localhost8000.*と表示されており、http://localhost8000以下のURLがコンテキストに含まれていることが分かります。(正規表現では「.」は任意の1文字、「*]は直前のパターンの0回以上の繰り返しを指すので、「http://localhost8000.*」はhttp://localhost8000以下の任意のURLを含んでいます)
次に、追加したコンテキストに対して、スコープが設定されていることを確認します。
スコープを設定することで、動的スキャンを行う際に、指定したスコープ以外に対して攻撃を行うのを防ぐことができます。
対象となるコンテキストを選択して、「スコープに含める」にチェックが入っている場合にはスコープが設定されていることを表しています。(デフォルトでスコープにチェックが入っていますが、もしチェックが入っていない場合には、「スコープに含める」欄をクリックしてください)
自動ログインの設定
認証機能がついているWebサイトを診断する場合には、事前にコンテキストに対して自動ログイン設定を行う必要があります。
サイト一覧から、ログインを行っているリクエストを右クリックして、「Flag as Context」をクリックします。すると、[コンテキスト名] : [認証方法] という形式の選択肢が表示されます。
日記アプリは下の画面のように、フォームでの認証を行っているので、
http://localhost:8000 : Form-Based Authenticationをクリックします。
「セッションプロパティ」を選択して、対象のコンテキストの「認証」欄を開きます。
Regex Patternを除いた、「認証」欄のそれぞれの項目に対して、自動的に値が設定されています。もし、本来とは異なる値が設定されている場合には修正をする必要があります。
各項目の説明をします。
- コンテキストで現在選択されている認証方法
複数の認証方法の中から、診断の対象となるサイトの認証方法を選択します。
今回はフォームでの認証を行うので、「Form-based Authentication」が選択されています。
- Login Form Target URL, URL to Get Login Page
対象となるサイトでのログイン画面のURLを指定します。今回は「https://localhost:8000/accounts/login/」が入力されています。
- Login Request POST Data
ログインリクエストを送信する際のPOSTデータを指定します。
- Usename Parameter, Password Parameter
自動ログインに利用するユーザー名とパラメータを選択します。
日記アプリではユーザー名、パスワードのパラメータはそれぞれ「login」, 「password」となるので、これらを選択します。
- Verification strategy
Check every resppnseを選択します。
- Regex Patter used to Identify Logged IN/OUT messages
ログイン/ログアウト処理が成功した場合のレスポンス文字列の正規表現を記述します。
それぞれの処理が成功したことを検出するために使用されます。それぞれの場合に必ず出る文字列を設定する必要があります。
ログインが成功した際には、ログアウトのリンクが画面上に表示されるので、
\Q<a class="nav-link" href="/accounts/logout/">Log Out</a>\E
をログイン判定の正規表現として設定し、
同様に、ログアウト時にはログインのリンクが表示されるので、
\Q<a class="nav-link" href="/accounts/login/">Log In</a>\E
を設定します。
(\Q ~ \Eで囲んだ文字列内のメタ文字はすべてエスケープされます)
自動ログインユーザーの設定
「セッションプロパティ」画面で、今回の検査の対象となるコンテキストの「ユーザー」欄を選択して、自動ログインに使用するユーザーを追加します。
ログインに使用するUsernemeとパスワードをそれぞれ指定して、新たにユーザーを作成します。ここで作成したユーザーは、後で診断を行う際に指定します。
強制ログインユーザーの設定
画面上部の錠前のアイコンをクリックして、錠前が閉じている状態にしてください。
動的スキャンの実行
実際に脆弱性診断を行います。「コンテキスト」欄の今回作成したコンテキストを右クリックして、「攻撃」をクリックします。
「動的スキャン」ウィンドウが開くので、攻撃対象となるコンテキストと自動ログインに使用するユーザーを指定します。ポリシーは「Default Policy」のままで大丈夫です。
「スキャンを開始」をクリックすると、脆弱性診断が始まります。
画面下部のバーが100%になったとき、脆弱性診断が完了します。
診断結果の確認
診断結果を記載したレポートは、画面上部の「レポート」をクリックして、「Generate Report」を選択することで生成されます。
レポートには、脆弱性診断を行った結果、判明した脆弱性が記載されています。
例えば、「Cookie No HttpOnly Flag」はCookieにHTTPOnly属性が付与されていないことを表しています。
DjangoでCookieに対して、HttpOnly属性を付与するためには https://stackoverflow.com/questions/3529695/how-do-i-set-httponly-cookie-in-django にあるように、Djangoの設定画面で
SESSION_COOKIE_HTTPONLY = True
とすることで実現できます。
まとめ
以下の手順で、OWASP ZAPによる脆弱性診断を行うことができました。
自動診断は非常に便利ですが、より精度の高い診断を行うためにはOWASP ZAPの手動診断機能やBurpSuite等の手動診断ツールを使用する必要があります。
参考文献
- 上野宣 「脆弱性診断スタートガイド」 第二版
- 徳丸浩 「安全なWebアプリケーションの作り方」 第二版
- 大高隆 「動かして学ぶDjango開発入門」
- 横瀬明仁 「現場で使えるDjangoの教科書 基礎編」 第二版