「ハッキング・ラボのつくりかた 完全版 仮想環境におけるハッカー体験学習」と「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践」(通称:徳丸本)を参考に、セキュリティの勉強を進めています。
前回は、以前 に行った 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 でした。
jquery.com
「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 という脆弱性を見ていきます。
指摘の説明では、Content-Security-Policy、X-Frame-Options のどちらも設定されていない、ということでした。
徳丸本では、クリックジャッキングという攻撃手法の説明の中で、X-Frame-Optionsヘッダについての説明があります。
簡単に言うと、クリックジャッキングとは、iframeタグを使って、罠内容を隠して、ボタンをクリックさせるという攻撃手法です。
徳丸本では、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
これをコメントアウトします。
あとは、文法チェックをして、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 レスポンスヘッダに設定されていない、という指摘のようです。
徳丸本では、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 のロゴを使わせていただきました。ありがとうございます。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。