このチャレンジについて
- 目的: セキュリティエンジニアとしての技術力向上
- 手段: シェルスクリプト作成を通じて学習
- 実施する事: 自動化,監視,ツール開発基礎学習など
#day4の目的:Webサイトの死活監視をスクリプト化
Webサーバーのログ解析について学習したいが、ターゲットのWEBサーバをそのためにイチからデプロイするのは、正直ちょっと面倒...。かといって、適当なWEBサーバを標的にして過負荷かけてしまった場合などを考えると課題が多い。
そこで今回は課題を絞り、「Webサイトが生きているか?」自動チェックするツール作りに一旦フォーカスします。セキュリティ評価の最初のステップである「偵察(リコネッサンス)」に該当します。(ターゲットが応答しなければ、何も始まらない)
利便性・拡張性を考慮し、リストにあるサイトの生存確認をまとめて行うツールとしました。
目次
- 実行結果
- 前提
- コード全文
- コードの詳細な解説
- 実行方法
- まとめ
実行結果
完成したスクリプトを実行した結果、以下のようになりました。urls.txt
に記述された各サイトのHTTPステータスがターミナルに一覧表示され、多数のターゲットの状態を迅速に把握できる結果となりました。
--- サイトのステータスチェックを開始 ---
[OK] : (200) https://example.com
[OK] : (200) https://www.google.com
[OK] : (200) https://jsonplaceholder.typicode.com/posts/1
[REDIRECT] : (301) http://google.com
[NOT FOUND]: (404) https://httpbin.org/status/404
[NOT FOUND]: (404) https://github.com/test-user-for-scripting-123/this-repo-does-not-exist
[ERROR] : (000) http://this-site-is-definitely-not-exist.xyz
--- チェック完了 ---
コード全文
/home/<your_user>/archive/day4/getHTTPStatus.sh
#!/bin/bash
# =============================================================
# Webサイト死活監視スクリプト (getHTTPStatus.sh)
#
# urls.txtからURLリストを読み込み、それぞれのHTTPステータスを
# 確認して結果を表示します。
# =============================================================
# --- 1. パスの設定 ---
# スクリプト自身のディレクトリを取得し、どこから実行されてもパスを解決できるようにする
SCRIPT_DIR=$(dirname "$0")
TARGET_LIST="$SCRIPT_DIR/urls.txt"
# --- 2. 事前チェック ---
# ターゲットリストが存在するかチェック
if [ ! -f "$TARGET_LIST" ]; then
echo "エラー: ターゲットリスト '$TARGET_LIST' が見つかりません。" >&2
exit 1
fi
# --- 3. メイン処理 ---
echo "--- サイトのステータスチェックを開始 ---"
while read -r url; do
# 空行または'#'で始まるコメント行はスキップする
if [ -z "$url" ] || [[ "$url" == \#* ]]; then
continue
fi
# curlでHTTPステータスコードを取得。-m 5でタイムアウトを5秒に設定。
status_code=$(curl -m 5 -s -o /dev/null -w "%{http_code}" "$url")
# ステータスコードに応じて結果を判定・表示
if [ "$status_code" -eq 200 ]; then
echo -e "[\e[32mOK\e[0m] : (\e[32m$status_code\e[0m) $url"
elif [ "$status_code" -eq 301 ] || [ "$status_code" -eq 302 ]; then
echo -e "[\e[33mREDIRECT\e[0m] : (\e[33m$status_code\e[0m) $url"
elif [ "$status_code" -eq 404 ]; then
echo -e "[\e[31mNOT FOUND\e[0m]: (\e[31m$status_code\e[0m) $url"
else
echo -e "[\e[31mERROR\e[0m] : (\e[31m$status_code\e[0m) $url"
fi
done < "$TARGET_LIST"
echo "--- チェック完了 ---"
コードの詳細な解説
本スクリプトは、複数のコンポーネントで構成されています。
-
パスの解決 (
SCRIPT_DIR=$(dirname "$0")
)この一行は、スクリプトをどこから実行しても安定して動作させるための重要な記述です。
-
問題点: もしこのパス解決の記述がなく、単に
urls.txt
のように相対パスでファイルを指定した場合、実行場所によって動作が変わるという問題が発生します。-
成功例:
cd /home/<your_user>/archive/day4
のようにスクリプトと同じ場所に移動してから./getHTTPStatus.sh
を実行した場合。カレントディレクトリにurls.txt
が存在するため、スクリプトは正しく動作します。 -
失敗例: ホームディレクトリ(
~
)から./archive/day4/getHTTPStatus.sh
を実行した場合。スクリプトはカレントディレクトリであるホームディレクトリ内を基準にurls.txt
を探しますが、そこにはファイルが存在しないためNo such file or directory
エラーとなります。
-
成功例:
-
解決策: この問題を避けるため、スクリプトに自分自身の場所を把握させ、それを基準に
urls.txt
のパスを解決します。 -
実装:
-
$0
: まず、特殊変数$0
には、実行されたスクリプト自身のパス(例:./archive/day4/getHTTPStatus.sh
)が格納されています。 -
dirname "$0"
: 次にdirname
コマンドが、$0
のパスからファイル名を取り除き、ディレクトリ部分(例:./archive/day4
)だけを抽出します。 -
SCRIPT_DIR=$(...)
: この抽出したディレクトリパスをSCRIPT_DIR
という変数に格納します。 -
TARGET_LIST="$SCRIPT_DIR/urls.txt"
: 最後に、このディレクトリパスとファイル名を結合し、urls.txt
へのフルパスを完成させることで、どこから実行しても安定した動作を保証します。
-
-
問題点: もしこのパス解決の記述がなく、単に
-
ループ処理 (
while read -r url; do ... done < "$TARGET_LIST"
)
while
ループとread
コマンドを組み合わせ、入力リダイレクト<
で指定された$TARGET_LIST
の内容を1行ずつ読み込みます。読み込まれた行はurl
変数に格納され、ループ内の処理が実行されます。ファイルの終端に達するとループは自動的に終了します。-r
オプションは、バックスラッシュを含む行をそのまま読み込むための推奨設定です。 -
入力フィルタリング (
if [ -z "$url" ] ...
)
while
ループの冒頭で、読み込んだ行が処理対象かを判定します。-z
演算子は変数が空(空行)であるかを、[[ "$url" == \#* ]]
は変数が#
で始まる(コメント行)かを判定します。continue
文は、以降の処理をスキップして次のループサイクルを開始します。 -
ステータスコード取得 (
curl ...
)
curl
コマンドでHTTPリクエストを送信します。各オプションは以下の機能を持ちます。-
-m 5
: 接続タイムアウトを5秒に設定します。応答のないサイトで処理が滞留するのを防ぎます。 -
-s
: 進捗メーターなどのサイレントモードを有効にします。 -
-o /dev/null
: レスポンスボディを破棄します。不要な出力を抑制します。 -
-w "%{http_code}"
: レスポンスヘッダからHTTPステータスコードのみを標準出力に書き出します。
-
-
条件分岐 (
if [ "$status_code" -eq 200 ]
)
$(...)
(コマンド置換)でcurl
の実行結果を格納した$status_code
変数を評価します。数値の比較には-eq
(equal)などの算術比較演算子を用います。これにより、ステータスコードに応じたメッセージの表示分けを実現しています。 -
色付き出力 (
echo -e
)ターミナルに色付きの文字を表示するには、「エスケープシーケンス」という特殊な文字列を使用します。
-
エスケープシーケンスとは: 画面に表示される文字ではなく、ターミナルに対する「命令」として解釈される文字列のことです。「文字色を赤に変えろ」「カーソルを一行上に動かせ」といった制御が可能で、多くは
\e[
から始まります。 -
色を付けるには:
-
書式:
\e[色のコードm
という形式で色を指定します。 -
色のコード:
31
が赤、32
が緑、33
が黄色など、色ごとに番号が決まっています。 -
リセット: 一度色を変えると、元に戻すまでその色が適用され続けます。
\e[0m
という特別なシーケンスを送ることで、文字色をデフォルトに戻すことができます。
-
書式:
-
スクリプトでの実装:
echo -e "[\e[32mOK\e[0m]..."
-
echo -e
:-e
オプションを付けることで、echo
はエスケープシーケンスを「命令」として解釈するようになります。 -
\e[32m
: これ以降の文字を緑色にする命令です。 -
OK
: 緑色で表示される文字列です。 -
\e[0m
: 文字色をデフォルトに戻す命令です。これにより、OK
という文字だけが緑色になります。
-
-
エスケープシーケンスとは: 画面に表示される文字ではなく、ターミナルに対する「命令」として解釈される文字列のことです。「文字色を赤に変えろ」「カーソルを一行上に動かせ」といった制御が可能で、多くは
実行方法
スクリプトに実行権限を付与し、実行します。
# 実行権限を付与
chmod 700 /home/<your_user>/archive/day4/getHTTPStatus.sh
# スクリプトを実行
/home/<your_user>/archive/day4/getHTTPStatus.sh
まとめ
#day4では、単純な死活監視から発展させ、より実践的な偵察ツールを作成しました。この過程で、倫理的なテスト対象の選定、実行環境を意識したパスの解決まで意識できた事は大きな収穫だな~と成長を感じる事ができました。
特に、基本的なコマンドをパイプで連携させること、そして引数やエラー処理、パスの解決策を組み込むことで、スクリプトが再利用可能なツールへと昇華するプロセスは、今後の開発において重要な知見となる気がしています。