パイプラインが失敗すると、チームは通常、完全なログ、テスト添付ファイル、結果バンドルを共有ストレージへアップロードし、別のエンジニアが調査を引き継ぎます。本当に危険なのは、目立つ TOKEN= という文字列ではありません。デバッグモードでエコーされたコマンド引数、リクエストヘッダー、一時的な署名パス、テストコードが収集した環境スナップショットです。クラウド Mac が長時間にわたって複数のジョブを処理すると、こうした情報がキャッシュやアーカイブにも残る可能性があります。そのため、ログの機密情報除去は、出力元、パイプライン、アップロードゲートの3か所で実施する必要があります。
ログのデータフローを最初に可視化する
最初から、あらゆるテキストを対象にした1本の正規表現を書こうとしてはいけません。まず、情報がどこから入り、どのプロセスを経由し、最終的にどこへ保存されるかを洗い出します。
| 入口 | よくある漏えい経路 | 優先する対策 |
|---|---|---|
| Shell | set -x が展開後の変数をエコーする |
機密情報を扱う処理の前にトレースを無効化する |
| コマンド引数 | 鍵やトークンがプロセス引数に直接現れる | アクセス権を制限した一時ファイルまたは標準入力を使う |
| 環境変数 | 診断スクリプトが環境全体を出力する | 許可リストに含まれる項目だけを出力する |
| テスト | リクエストヘッダーやアカウントデータが添付ファイルに書き込まれる | テストヘルパー層で一括して除去する |
| 結果バンドル | スクリーンショット、アクティビティログ、診断ファイルがまとめてアップロードされる | エクスポート後にスキャンしてから公開する |
機密情報を除去しても、すでに漏えいした鍵が再び安全になるわけではありません。実値が共有可能なログに一度でも入ったことを確認した場合は、その成果物の公開を中止し、鍵の種類に応じてローテーションしてください。
まず、OakVM ノード上でジョブごとに独立したディレクトリを作成し、現在のユーザーだけが読み書きできる権限を設定します。ログ、結果バンドル、アップロード予定ファイルは別々に配置し、スキャン済みファイルと元ファイルが混在しないようにします。
umask 077
run_id="$(date -u +%Y%m%dT%H%M%SZ)-$$"
root="$HOME/ci-runs/$run_id"
mkdir -p "$root/raw" "$root/safe" "$root/results"
出力元で漏えい経路を遮断する
シークレットを引数に渡さない
プロセス引数は、診断ツール、クラッシュレポート、同じユーザーが起動したスクリプトから読み取られる可能性があります。社内アップロードスクリプトを呼び出す際に、--token "$TOKEN" と書いてはいけません。より安全なのは、スクリプト側で権限が 600 の一時ファイルを読み取り、使用後すぐに削除する方法です。ツールが標準入力に対応している場合は、標準入力経由で渡すこともできます。
Shell トレースはデフォルトで無効にします。通常のコマンドを確認する必要がある場合も、機密変数に触れない短い区間だけで有効にし、認証情報を読み込む前に set +x を実行してください。また、環境全体を直接出力することは禁止し、診断情報は許可リスト方式に切り替えます。
printf 'PATH=%s
' "$PATH"
printf 'DEVELOPER_DIR=%s
' "$DEVELOPER_DIR"
xcodebuild -version
sw_vers
パスも機密情報として扱う必要があります。ユーザー名、プロジェクトのコードネーム、一時的な署名ディレクトリから、チーム構成が推測されることがあります。ログを公開する前に作業ルートを $WORKSPACE に置換できますが、トラブルシューティングに必要なファイル名と行番号は残してください。
失敗を見落とさない機密情報除去パイプラインを構築する
置換では、password や secret といったフィールド名だけを探すのではなく、実際のシークレット値との一致を優先します。フィールド名による検出は誤検知が起きやすいうえ、ラベルのないトークンを防げません。次のフィルターは、アクセスを制限したファイルからマスク対象の値を読み取り、短い値が長い値の一致を壊さないよう、長い順に置換します。
import os
import sys
from pathlib import Path
secret_file = Path(os.environ["REDACT_FILE"])
values = [
line.rstrip("
")
for line in secret_file.read_text(encoding="utf-8").splitlines()
if line.strip()
]
values.sort(key=len, reverse=True)
for line in sys.stdin:
for value in values:
line = line.replace(value, "[REDACTED]")
sys.stdout.write(line)
ビルドの実行時には、xcodebuild の実際の終了コードを維持する必要があります。パイプライン末尾の tee だけを参照すると、失敗したビルドが成功と誤判定される可能性があります。
#!/bin/bash
set -o pipefail
set +x
REDACT_FILE="$root/secrets.txt" \
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-resultBundlePath "$root/results/App.xcresult" \
build 2>&1 |
python3 ci/redact.py |
tee "$root/safe/build.log"
statuses=("${PIPESTATUS[@]}")
exit "${statuses[0]}"
secrets.txt の権限は必ず 600 に設定し、ジョブ終了時のフックで削除します。複数行の秘密鍵は、行単位の置換には適していません。正しい対策は出力元で表示を禁止し、テキスト中に現れる可能性がある安定した識別値だけをマスクファイルへ追加することです。
結果バンドルとテスト添付ファイルをスキャンする
テキストログに問題がないからといって、ジョブ全体をアップロードできるとは限りません。結果バンドルには、テストのスクリーンショット、失敗時の添付ファイル、アクティビティログ、診断情報が含まれる可能性があります。まず隔離ディレクトリへコピーし、共有が本当に必要な内容だけをエクスポートしてから、そのエクスポート先をスキャンします。結果バンドル全体をデフォルトで公開してはいけません。
2種類のルールで誤検知を減らす
1つ目のルールは、そのジョブで実際に使用したシークレット値を照合し、一致した場合は即座にブロックします。2つ目のルールは、認証ヘッダー、秘密鍵の境界、認証情報を含む URL、長いトークンらしき文字列など、リスクの高い構造を検査します。構造ルールに一致した場合は人手によるレビューへ回し、token という文字列をすべて自動的に漏えいと判定しないでください。ソースファイル名やテストの説明にも、この単語が含まれる可能性があります。
スキャンレポートには、ファイルパス、行番号、ルール番号だけを記録し、一致した原文を再度コピーしてはいけません。バイナリ添付ファイルは、まず種類を判別します。安全に解析できないファイルは、strings で抽出してそのまま公開するのではなく、デフォルトで共有成果物から除外します。
機密情報除去をリリースゲートにする
保守可能なゲートには、少なくとも4つの状態が必要です。ビルドの終了コード、実値の完全一致スキャン、構造ルールのレビュー、成果物の許可リストです。いずれか1つでも完了していなければ、アップロード処理を開始してはいけません。
各ジョブの終了時には、次の項目を確認することを推奨します。
- 元データ用ディレクトリの権限が、引き続き現在のユーザーだけに制限されているか。
- ログに作業ルート、認証ヘッダー、実際のシークレット値が残っていないか。
- 結果バンドルに無関係なスクリーンショット、ネットワークレスポンス、環境スナップショットが含まれていないか。
- アップロード一覧が、機密情報除去済みログ、必要なレポート、明示的に選択した添付ファイルだけで構成されているか。
- クリーンアップスクリプトがシークレットファイルを削除しつつ、調査に必要な安全なコピーまで削除していないか。
最後に、逆方向のテストを1回実施します。一時ジョブへ専用の偽トークンを注入し、通常出力、標準エラー出力、テスト添付ファイルをそれぞれ経由させて、ゲートが確実にブロックすることを確認します。続いて、ビルドを意図的に非ゼロのステータスで終了させ、機密情報除去パイプラインが失敗を成功に変えてしまわないことを確認します。この2種類のテストが両方とも成功して初めて、ログのセキュリティ対策が実際のエンジニアリングプロセスへ組み込まれたと判断できます。
よくある質問
アップロード直前に文字列を置換するだけで十分ですか?
十分ではありません。値が端末出力や一時ファイル、結果バンドルに残るため、先にトレースを無効化し、コマンド引数へ機密値を渡さない設計が必要です。
フィルター処理後も xcodebuild の終了コードを維持する方法は?
Bash で pipefail を有効にし、パイプライン直後の PIPESTATUS[0] を保存します。tee やフィルターの終了コードをビルド結果に使わないでください。
テキストログ以外に何を検査すべきですか?
結果バンドル、テスト添付ファイル、診断アーカイブ、書き出した設定ファイル、独自スクリプトのレポートを確認します。
次のビルドを専用物理ノードで実行
OakVM M4またはOakVM M4 Proを選択し、仮想マシンではないクラウド Macを日単位・週単位・月単位・四半期単位でレンタルできます。ノードの実際の利用可否はコンソールのリアルタイム情報をご確認ください。