0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで画面録画の秘密情報を監査する

0
Posted at

Pythonで画面録画の秘密情報を監査する

午前中、管理画面で同じ操作を二度繰り返していた。定型作業なら、画面を一度見せてエージェント用の手順にできると助かる。けれど録画データを見返すと、URL のクエリ、ウィンドウ名、クリップボードの断片が一緒に残っていた。

画面操作からスキルを作る仕組みでは、手順の正しさより先に、解析へ渡す記録を止める場所が要る。Microsoft の Skill Recorder も、解析時にはイベント時系列、ウィンドウや文書のタイトル、URL、クリップボードのプレビュー、画面画像、ナレーションをクラウドへ送ると明記している。ローカル録画だからといって、その後の解析までローカルとは限らない。README の注意書きはかなり具体的だ。

ここでは JSON にしたイベント時系列を解析の直前で弾く、小さな監査を置く。これは秘密を安全に消すコードではない。検出したら送信を中断し、録画を捨てて安全なダミー値で撮り直すためのゲートにする。

解析前に JSON を止める Python

入力は session.json とし、辞書と配列のどこに文字列があっても調べる。値は標準出力に出さない。CI のログにトークンをもう一度残す事故を避けるためだ。

import json
import re
import sys
from pathlib import Path

RULES = {
    "URL の認証情報": re.compile(r"://[^/\s:@]+:[^/\s@]+@", re.I),
    "URL の秘密パラメータ": re.compile(
        r"[?&](?:access_token|api[_-]?key|authorization|password|secret|token)=[^&#\s]+",
        re.I,
    ),
    "Bearer トークン": re.compile(r"\bBearer\s+[A-Za-z0-9._~+/-]{16,}", re.I),
    "GitHub トークン": re.compile(
        r"\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,})\b"
    ),
    "秘密値らしい代入": re.compile(
        r"\b(?:api[_-]?key|password|secret|token)\s*=\s*[^\s]{8,}", re.I
    ),
}

def walk(value, path="$"):
    if isinstance(value, dict):
        for key, child in value.items():
            yield from walk(child, f"{path}.{key}")
    elif isinstance(value, list):
        for index, child in enumerate(value):
            yield from walk(child, f"{path}[{index}]")
    elif isinstance(value, str):
        yield path, value

def audit(session):
    findings = []
    for path, text in walk(session):
        for name, pattern in RULES.items():
            if pattern.search(text):
                findings.append({"path": path, "rule": name})
                break
    return findings

findings = audit(json.loads(Path(sys.argv[1]).read_text()))
for item in findings:
    print(f"BLOCK {item['rule']}: {item['path']}")
raise SystemExit(1 if findings else 0)

ファイル名を recording_audit.py として、解析処理の前に実行する。

python recording_audit.py session.json

手元で確認した入力と出力

URL の token、クリップボードの Bearer 値、.env を開いたウィンドウ名、URL に埋めた Basic 認証の4件を含む JSON で動かした。検証時の出力は次の通りだった。

BLOCK URL の秘密パラメータ: $.events[0].url
BLOCK Bearer トークン: $.events[1].preview
BLOCK 秘密値らしい代入: $.events[2].title
BLOCK URL の認証情報: $.events[3].url

検出した値そのものは表示しない。ここは地味だが大事で、print(text) をデバッグ用に足すと監査の意味がなくなる。終了コードが 1 なので、ラッパースクリプトや CI からは成功時だけ次の解析 API を呼べる。

録画を安全にするコードではない

次の流れで止める。

画面画像や動画に写ったシークレットは、この JSON 監査では読めない。OCR を足して万能にしようとすると、今度は誤検出時の取り回しが悪くなる。自分は録画前に本番の管理画面を閉じ、ダミーの値だけを使う専用ブラウザープロファイルで操作を取り直す方が早かった。

もう一つ落とし穴がある。パターン一致は漏れを減らす補助でしかない。sid や社内固有のパラメータ名はこのままでは拾えない。運用に入れるなら、社内のトークン接頭辞と禁止ホスト名を RULES に追加し、サンプル JSON をテストとして残す。検出漏れを後から再現できる形にするためだ。

おわりに

画面録画からのスキル化は、UI をそのまま再生する話ではない。記録された事実から、別の入力にも通る手順を組み立てる工程になる。その境界を越えるデータに秘密が混じるなら、手順がどれほど便利でも先へ流せない。

まずは解析の直前に、この程度の失敗する監査を置く。次に録画環境を分ける。この二つを先に決めておけば、スキルを増やしたあとに「過去の記録を全部見直す」作業をかなり減らせる。

参考: https://github.com/microsoft/skill-recorder

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?