セキュリティ回帰を調査して再発を防ぐ

サイバーセキュリティBeginner
オンラインで実践に進む

はじめに

一時的なデバッグ設定が、新しい DeskLite サービスのバージョンで復活してしまいました。リクエストは引き続き成功しますが、運用ログに偽の認証情報が含まれるようになっています。機能の成功チェックだけでは、この回帰を検出できませんでした。

このラボでは、実際のリクエストの証拠をたどり、新たな漏えいを止め、露出した認証情報を置き換えて、実際に漏えいする候補を阻止するアサーションを追加します。過去のログは証拠として残します。調査文書は不要です。ローカルの発行元、クライアント、サービス、比較対象、成果物用ヘルパーは用意されています。

実際のインシデントイベントを見つける

このステップでは、リクエストと、現在のログ記録バージョンおよび設定を関連付けます。

cd /srv/ticket-regression

tail -n 5 は、最後の 5 行を読み取り、表示範囲を小さく保ちます。用意されたサービスログを、許可された管理者権限で読み取ります。

sudo tail -n 5 /opt/labex-ticket-regression/requests.log

request_id=initial-001、version=logging-v2、path=/tickets、status=200 を探してください。この過去のリクエストは成功していますが、同じ行に Authorization と古い架空の Token が含まれています。すべての値は、この独立したラボだけのものです。

grep は、指定したテキストパターンを含む行を表示します。一重引用符は、この文字どおりのパターンをひとまとまりにします。

sudo grep 'request_id=initial-001' /opt/labex-ticket-regression/requests.log
cat logging.conf

include_authorization = true が、機密フィールドの原因を説明しています。イベントの時刻、リクエスト ID、バージョン、ステータスによって、観察した動作を有効な設定に結び付けられます。元のログは維持してください。消去すると、将来の漏えいを防ぐのではなく、証拠を取り除くことになります。

新しいイベントへの機密フィールドの記録を止める

このステップでは、有用な監査フィールドを維持しながら、ログ記録方針を変更します。

nano logging.conf

次の内容を使います。

[logging]
include_authorization = false

Ctrl+O、Enter、Ctrl+X で保存して終了します。用意された再起動の入口を使い、保存した設定を適用します。

bash restart.sh

新しいヘルスチェックリクエストに、分かっている ID を与えます。X-Request-ID は、その新しいイベントを対応付けるためのリクエストヘッダーです。

curl -H 'X-Request-ID: after-policy-fix' http://127.0.0.1:8080/health
sudo grep 'request_id=after-policy-fix' /opt/labex-ticket-regression/requests.log

時刻、バージョン、ID、パス、status=200 があり、Authorization フィールドはないはずです。古い initial-001 イベントは残っています。すべての過去の行に秘密情報がないことを求めるのではなく、新しいイベントをそれぞれの ID で判断してください。

露出した認証情報を失効させて置き換える

このステップでは、学習済みの発行元のワークフローを使い、古い Token の効力を取り除きます。

ログ記録方針を変更しても、露出した値は失効しません。その管理用 ID を失効させます。

course-token revoke legacy
curl -i -H 'Authorization: Bearer labex_012345abcdefghijklmnopqrstuvwxyz' http://127.0.0.1:8080/tickets

401 が返るはずです。次に、代わりのトークンを、値を表示せずに発行します。

course-token issue /srv/ticket-regression/api.token

権限と Token のラボの、管理者/クライアントのグループのパターンで保護します。

sudo chown root:deskclient api.token
sudo chmod 640 api.token

値は発行元が作成しました。設定するのは、そのアクセス権だけです。テストのソース、クライアントスクリプト、レポートにコピーしないでください。

ファイルを読み取るクライアントを復旧する

このステップでは、既存のクライアントが、保護された代わりのトークンを使うようにします。

nano sender.sh

前に学んだのと同じ、ファイルを読み取る形式を保存します。

#!/bin/bash
exec sudo -u deskclient /usr/bin/python3 /opt/labex-ticket-regression/send.py --token-file /srv/ticket-regression/api.token

Ctrl+O、Enter、Ctrl+X を使います。

bash sender.sh

HTTP 200 と、元の Printer offline チケットが返るはずです。最近の運用イベントを読み取ります。

sudo tail -n 5 /opt/labex-ticket-regression/requests.log

新しい成功リクエストには、認証情報の値を含めずに、監査フィールドが残ります。古い漏えい値は、その効力が取り除かれた状態で、保存されたインシデント履歴に残ることがあります。この区別により、証拠を削除することも、過去の漏えいを現在も認証に成功する経路と扱うことも避けられます。

既存のテストが漏えいを見逃した理由を確認する

このステップでは、新しい要件を追加する前に、小さなテストテンプレートを確認します。

cat test_security.py

成功ケースのテストは、テスト作成のラボのリクエストテンプレートを使っています。用意された api ヘルパーは、対象の認証情報の取得元から token を用意し、実際の HTTP ステータス、本文、そのリクエストの実際のログイベントを返します。"Bearer " + token は、2 つの文字列をヘッダー値につなぎます。値をソースにコピーしているわけではありません。

2 つの独立した比較対象は、公開の偽のテスト用 Token を使います。good は機密ログフィールドを省略し、logging-regression は意図的にテスト用 Token を記録します。どちらも、新しい運用用の代わりのトークンを使ったり、露出させたりしません。

course-regression-tests good
course-regression-tests logging-regression

現在は、両方が test_valid_token に合格します。どちらの実装でも、ステータスと業務データは正しいままです。不足している要件は、response["log"] の実際のイベントに関するものです。ヘルパーの各リクエストは一意の ID を使うため、返されるログは、無関係な過去のイベントではなく、その新しいリクエストに属します。

ログの機密情報除去を確認するアサーションを追加する

このステップでは、新しいイベントについて、存在してはいけない内容と、存在すべき内容の両方をテストに追加します。

Python の in は、ある文字列が別の文字列内にあるかを確認します。not in は、それがないことを要求します。これらは、手作業で用意した証拠ファイルではなく、実際に返されたログテキストへのアサーションです。

nano test_security.py

成功ケースのテストを維持し、次の完全なファイルを使います。

from api import request, token

def test_valid_token():
    response = request("/tickets", {"Authorization": "Bearer " + token})
    assert response["status"] == 200
    assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}

def test_log_is_redacted():
    response = request("/tickets", {"Authorization": "Bearer " + token})
    assert response["status"] == 200
    assert "Authorization" not in response["log"]
    assert token not in response["log"]
    assert "request_id=" in response["log"]
    assert "path=/tickets" in response["log"]
    assert "status=200" in response["log"]

Ctrl+O、Enter、Ctrl+X で保存して終了します。新しいテストは、機密フィールドと認証情報の値を拒否しながら、リクエスト ID、業務パス、成功ステータスを要求します。ログを空にすると、これらの存在要件を満たせません。

ヘルパーは、対象ごとの Token を内部で用意します。通常のサービステストは、保護された代わりのトークンを読み取ります。比較対象は、公開の偽のテスト用データを使います。どちらの値も、このテストファイルに書くべきものではありません。

テストが既知の回帰を検出することを示す

このステップでは、安全な動作と意図的に漏えいする動作に対して、新しいアサーションを検証します。

course-regression-tests good

両方のテストが合格するはずです。

course-regression-tests logging-regression

test_log_is_redacted だけが AssertionError で失敗し、終了コード 1 を返すはずです。これは意図した検出であり、アサーションを弱めるよう求めているわけではありません。成功ケースの業務テストは、引き続き合格する必要があります。

course-regression-tests live

実際に修正したサービスに対して、両方が合格するはずです。これらのテストは、新しいリクエストのイベントだけを確認します。元の漏えいした過去のイベントは、意図的に保存されています。構文エラー、接続エラー、無条件の失敗では、機密情報の除去を効果的に確認するテスト範囲は示せません。

新しいテストを候補のデリバリーにつなぐ

このステップでは、新しいアサーションが失敗した場合に、将来の候補がデリバリーされるのを防ぎます。

用意されたゲートは、前のゲートのラボと同じチェック/ビルド/テスト/デプロイの構造を使っています。build.sh は、元のサーバーと現在のログ記録方針をパッケージ化し、その実際の候補を別のローカルポートで起動します。既存の test.sh は、有効な Token、Token なし、誤った Token の動作を確認しますが、ログは確認しません。

nano gate.sh

ビルドの後、既存の機能テストの前に、回帰テストの呼び出しを追加します。次の内容を使います。

#!/bin/bash
bash checks.sh
status=$?
if [ "$status" -eq 1 ]; then
    exit 1
fi
if [ "$status" -ne 0 ]; then
    exit 2
fi
bash build.sh || exit 2
bash regression.sh candidate || exit 2
bash test.sh || exit 2
bash deploy.sh

Ctrl+O、Enter、Ctrl+X で保存して終了します。candidate は、実際に直前にビルドしたアプリケーションを選ぶため、追加したアサーションは、これからデプロイする成果物を守ります。|| exit 2 は、そのテストが失敗した場合にデリバリーを停止します。

bash gate.sh

認証情報の事前チェック、実際の候補のビルド、新しい回帰テストの合格、機能テストの合格、その候補の運用用ローカルポートへのデプロイが行われるはずです。使われないテストファイルを残すだけでは、この再発防止は実現できません。

新しいイベントと保存した履歴を確認する

このステップでは、デリバリーした動作を検証し、レポートファイルを作成せずに証拠を振り返ります。

bash sender.sh
sudo tail -n 5 /opt/labex-ticket-regression/requests.log

新しい運用イベントには、認証情報を含めずに、時刻、実際にデリバリーしたバージョン、リクエスト ID、パス、ステータスを残す必要があります。デリバリーしたバージョンは、元の logging-v2 ラベルではなく、成果物のハッシュになっています。

sudo grep 'request_id=initial-001' /opt/labex-ticket-regression/requests.log

元のインシデントイベントは存在しています。その古い Token は失効しており、保護された代わりのトークンは動作します。別に用意された意図的な回帰の比較対象は、学習用の比較であり、デプロイされたサービスではありません。

どのイベントが問題を明らかにしたか、どの設定が原因だったか、なぜ方針の修正だけでは不十分だったか、新しい候補へのアサーションがどのように再発を防ぐかを、自分に説明してみてください。コースのチェックは、実際に回帰した候補を試し、ゲートがそれを阻止することと、以前の安全な実行中成果物が維持されることを確認します。固定形式の調査文書は不要です。

まとめ

実際のインシデントイベントをログ記録方針に結び付け、新しい漏えいを止め、露出した認証情報を失効させて置き換え、過去の証拠を保存しました。新しいアサーションは、意図的な回帰を検出し、デリバリーゲート内で実際の候補に対して実行されます。これにより、将来の候補が失敗しても、安全な稼働状態を維持できます。