「」と「」(通称:徳丸本)を参考に、セキュリティの勉強を進めています。
前回は、以前 に行った OWASP ZAP の自動脆弱性スキャンの結果の「オープンリダイレクト」について、分析と対策までやりました。
今回は、リスク中の内容を見ていきます。
それでは、やっていきます。
参考文献
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて ← 今回
徳丸本の環境構築については、以下の第9回でやりました。
daisuke20240310.hatenablog.com
また、徳丸本が用意してくれている、脆弱なアプリケーション Bad Todo の準備については、以下の第12回でやりました。今回も、この環境を使ってやっていきます。
daisuke20240310.hatenablog.com
リスク中の検出結果の確認
リスク中の内容を見ていきます。いくつか出ていますが、JQuery のバージョンが古い、とか、ブラウザのセキュリティ機能を強化するためのHTTPレスポンスヘッダに指定がない、など、直接的な影響のない脆弱性という感じです。
まず、ディレクトリブラウジングについて見ていきます。
ディレクトリ・リスティングの脆弱性の分析と再現
脆弱性スキャンの指摘結果には、ディレクトリブラウジングという名前の指摘になっていますが、徳丸本では、ディレクトリ・リスティングという名前で説明がされています。ここでは、徳丸本に合わせて、ディレクトリ・リスティングと呼ぶことにします。
これはよくある内容で、簡単に再現できたので、分析と再現をまとめてやっていきます。
以下のように、ディレクトリを指定すると、そのディレクトリの内容が見えてしまう脆弱性です。

これは、単純に、Webサーバ(Bad Todo アプリでは Apache)に対する設定がよくありません。ディレクトリを指定されたときに、ディレクトリの内容が見えないように設定しなければいけません。
ディレクトリ・リスティングの脆弱性の対策
Apache の設定を変えて、ディレクトリ・リスティングを禁止します。
Bad Todo アプリは、Apache で動いていて、以下の対応でディレクトリ・リスティングを禁止できると思います。
Apache の設定ファイルは、以下の /etc/apache2/apache2.conf が有効になってそうでした。
<Directory /var/www/> Options Indexes FollowSymLinks ExecCGI AllowOverride None Require all granted <FilesMatch "\.php$"> SetHandler application/x-httpd-php-5.3.3 Action application/x-httpd-php-5.3.3 /cgi-bin/php-5.3.3 </FilesMatch> <FilesMatch \.cgi$> SetHandler cgi-script </FilesMatch> </Directory>
ディレクトリ・リスティングを禁止するには、Options Indexes FollowSymLinks ExecCGI の Indexes を削除すればいいようです。
実際に、Indexes を削除して、以下のように、Apache を再起動したところ、ディレクトリ・リスティングを禁止できていました。
$ sudo service apache2 restart

自動脆弱性スキャンの再実行
では、自動脆弱性スキャンを実行します。ディレクトリブラウジングの指摘は無くなっていました。対策は成功したようです。急にリスク中が減った気もしますが、まぁいいですかね。

リスク中の検出結果の確認(続き)
続いて、Vulnerable JS Library(JQuery のバージョンが古い)を見ていきます。
Vulnerable JS Libraryの脆弱性の分析
version 1.8.3 は脆弱だと指摘されています。徳丸本でも、この件について言及があり、wasbook には、version 3.2.1 も含まれています。

Vulnerable JS Libraryの脆弱性の対策
ソースコードを検索すると、JQuery の参照は、todolist.php だけのようです。
以下のように修正しました。
--- todo.org/todolist.php 2018-08-16 12:03:14.000000000 +0900 +++ todo.change/todolist.php 2024-08-17 18:25:39.000000000 +0900 @@ -19,7 +19,7 @@ ?><html> <head> <link rel="stylesheet" type="text/css" href="css/common.css"> -<script src="../js/jquery-1.8.3.js"></script> +<script src="../js/jquery-3.2.1.min.js"></script> <title>一覧</title> </head> <body>
その後、Firefox で Bad Todo の todolist.php をアクセスしていると、OWASP ZAP で、version 3.2.1 is vulnerable. と、まだ指摘が出てしまいました。version 3.3.1 でも同様でした。
仕方ないので、最新版を以下の公式サイトからダウンロードします。現時点の最新版は、v3.7.1 でした。
「Download JQuery」というボタンをクリックして、「Download JQuery 3.7.1」というボタンをクリックします。すると、「jquery-3.7.1.min.js」が表示されるので、右クリックして名前を付けて保存を押し、保存したファイルを wasbook にアップロードします。
ソースコードは、上記と同様に <script src="../js/jquery-3.7.1.min.js"></script> に変更します。
これで、OWASP ZAP で指摘が出なくなりました。
Missing Anti-clickjacking Headerの脆弱性の分析
Missing Anti-clickjacking Header という脆弱性を見ていきます。
指摘の説明では、Content-Security-Policy、X-Frame-Options のどちらも設定されていない、ということでした。

徳丸本では、クリックジャッキングという攻撃手法の説明の中で、X-Frame-Optionsヘッダについての説明があります。
簡単に言うと、クリックジャッキングとは、iframeタグを使って、罠内容を隠して、ボタンをクリックさせるという攻撃手法です。
Missing Anti-clickjacking Headerの脆弱性の対策
徳丸本では、X-Frame-Optionsヘッダを設定すれば、iframe、frame を禁止できるとあります。
サイトとして、これらのタグを使用していないのであれば、常に X-Frame-Optionsヘッダを出力するように、Webサーバ(Apache)に設定も可能ということです。
具体的には、以下の一文を追加するだけでした。
Header always set X-Frame-Options "SAMEORIGIN"
編集が出来たら、文法チェックを行い、Apache を再起動します。
$ sudo apache2ctl configtest AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message Syntax OK $ sudo service apache2 restart
HTTPレスポンスを確認したところ、ちゃんと X-Frame-Options が追加されていました。

Hidden File Foundの脆弱性の分析
次は、Hidden File Foundの脆弱性を見ていきます。
これは、重要なファイルが検出されたということのようです。
phpinfo.php と server-status の2ファイルが指摘されています。

phpinfo.php は、wasbook が提供しているファイルなので問題ありません。
server-status の方は、以下のように、Apache で用意されている Webサーバの状態を確認できるもののようです。

Hidden File Foundの脆弱性の対策
phpinfo.php の方は意図的ということで対策はしないことにします。
server-status の方は、一応、対策を行います。
Apache の設定で、無効化すればいいようです。
/etc/apache2/mods-available/status.load は、以下のようになっています。
LoadModule status_module /usr/lib/apache2/modules/mod_status.so
これをコメントアウトします。
#LoadModule status_module /usr/lib/apache2/modules/mod_status.so
あとは、文法チェックをして、Apache を再起動します。
$ sudo apache2ctl configtest AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message Syntax OK $ sudo service apache2 restart
Content Security Policy (CSP) Header Not Setの脆弱性の分析
Content-Security-Policy レスポンスヘッダに設定されていない、という指摘のようです。

徳丸本では、Content-Security-Policy について説明がされていて、これを指定すると、XSS攻撃に対する防御策として期待ができる、と書かれていますが、一方で、JavaScript も禁止されるため、現状は、Content-Security-Policy の動向に注視する、と書かれていました。
これについては、対策は行わないことにします。
自動脆弱性スキャンの再実行
この時点で、自動脆弱性スキャンを再実行します。

まだ対策を行っていない、アンチCSRFトークンが使用されていない、以外は、想定通り、指摘がなくなっていました。
アンチCSRFトークンが使用されていないの脆弱性の分析
最後は、アンチCSRFトークンが使用されていないの脆弱性です。

CSRF とは、クロスサイト・リクエストフォージェリの略で、クロスサイトスクリプティングとよく似ている脆弱性です。
一方で、XSS に比べて、対策はとても難しい脆弱性のようです。例えば、罠サイトを経由させることが前提となりますが、パスワード変更を行う POSTリクエストに対して、CSRF攻撃をしかけた場合と、正規のサイトでパスワード変更を行う POSTリクエストは、Referer 以外に、区別ができないとのことです。
そこで、CSRFトークンとは、セッション変数に乱数(トークン)を設定しておき、パスワード変更を行うリクエストのフォームに、hiddenパラメータとして、同じ乱数を渡します。
パスワード変更を受けるページでは、セッション変数の乱数が設定されていることを確認し、その乱数と hiddenパラメータの乱数が一致することを確認します。
これにより、罠サイトからの POSTリクエストではないことが確認できます。これがアンチCSRFトークンです。
CSRF に対策を行うには、POSTリクエスト全てを対象にする場合、大がかりな対応が必要になります。一方で、外部リンクからのリクエストを全て禁止にするわけにいかないケースもあります。
よって、徳丸本では、重要なところだけ CSRF の対策を行うことを推奨していました。
指摘されている 7件について詳しく見ていきます。
| No. | リクエスト | 用途 | 判断と理由 |
|---|---|---|---|
| 1 | POST | ログイン | データベースを変更しないPOSTリクエストなので対策不要 |
| 2 | GET | TODOの検索 | GETリクエストなので対策不要 |
| 3 | POST | TODOの削除/完了 | データベースを変更するので対策必要 |
| 4 | GET | TODOの検索 | No.2と同じ |
| 5 | POST | TODOの削除/完了 | No.3と同じ |
| 6 | GET | TODOの検索 | No.2と同じ |
| 7 | POST | TODOの削除/完了 | No.3と同じ |
というわけで、対策が必要なのは1か所となりました。
アンチCSRFトークンが使用されていないの脆弱性の対策
徳丸本のソースコードの /45/45-002a.php と /45/45-003a.php に、CSRF対策のトークンを埋め込んだ実装があるので、この内容を使わせていただきます。
修正するソースコードは、todolist.php と editlist.php の2ファイルです。
ここで1点注意です。自動脆弱性スキャンを実行する前に、アンチCSRFトークンを、ツール→オプション... で設定したと思います。ここで「todotoken」という名前で設定しました。トークンの名前にはこの名前を使う必要があります。違う名前だと正しく診断できないようです。
まず、todolist.php です。今回の修正内容だけを貼ります。ログインしている場合にだけ、トークンの準備をしています。また、hiddenパラメータにトークンを設定しています。
--- todo.org/todolist.php 2018-08-16 12:03:14.000000000 +0900 +++ todo.change/todolist.php 2024-08-17 22:09:01.000000000 +0900 @@ -8,6 +8,15 @@ if (empty($reqid)) $reqid = -1; + if (is_loggedin()) { + if (empty($_SESSION['todotoken'])) { // トークンが空なら生成 + $todotoken = bin2hex(openssl_random_pseudo_bytes(24)); + $_SESSION['todotoken'] = $todotoken; + } else { // トークンがもともとあればそれを使う + $todotoken = $_SESSION['todotoken']; + } + } + try { $dbh = dblogin(); $sql = "SELECT todos.id, users.userid, todo, c_date, due_date, done, org_filename, real_filename, public FROM todos INNER JOIN users ON users.id=todos.owner AND (todos.owner=? OR ?) AND (todos.owner = ? OR todos.public > 0 OR ? > 0)"; @@ -74,6 +83,7 @@ } ?> </table><br> + <input type="hidden" name="todotoken" value="<?php echo htmlspecialchars($todotoken, ENT_COMPAT, 'UTF-8'); ?>"> <button type="submit" name="process" value="dellist">削除</button> <button type="submit" name="process" value="donelist">完了</button> <button type="submit" name="process" value="exportlist">エクスポート</button>
次に、editlist.php の修正点です。hiddenパラメータのトークンとセッション変数のトークンを取得します。error_log() はデバッグ用で両方のトークンをログ出力しています。hiddenパラメータのトークンが設定されていることと、両方のトークンが一致していることを条件に処理を継続します。条件が満たされない場合はエラー終了します。
--- todo.org/editlist.php 2018-08-19 10:26:23.000000000 +0900 +++ todo.change/editlist.php 2024-08-17 22:09:26.000000000 +0900 @@ -3,6 +3,13 @@ require_loggedin(); $id = $user->get_id(); +$p_token = filter_input(INPUT_POST, 'todotoken'); +$s_token = @$_SESSION['todotoken']; +error_log("p_token=" . $p_token . ", s_token=" . $s_token); +if (empty($p_token) || $p_token !== $s_token) { + die('正規の画面からご使用ください'); // 適当なエラーメッセージを表示する +} + $ids = @$_POST['id']; if (empty($ids)) { die('項目をチェックして下さい');
ログ出力は以下のような感じです。
p_token=1bc99a12b0d2d66f2a004f04c9795ab2eb4100c43c8f030f, s_token=1bc99a12b0d2d66f2a004f04c9795ab2eb4100c43c8f030f
2つのトークンが一致していることが分かります。
自動脆弱性スキャンの再実行
最後に、自動脆弱性スキャンを再実行します。
個数が合わないですが、5件は、ログイン、TODO の検索のみでした。対策は成功したようです。

だいぶ長かったですが、リスク中の対策は以上です。
おわりに
今回は、自動脆弱性スキャンのリスク中の脆弱性について、再現と対策を行いました。
次回はどうしましょうか。。
今回は、Chromium のロゴを使わせていただきました。ありがとうございます。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。