ブラウザに「Profile 1」という空のプロファイルを作り、Login Data も Cookies も無いことを確認した。今日の作業で一番効いたのは、この地味な確認だった。
CloudflareがAIエージェント向けのヘッドレスブラウザ Kitesurf を発表した、というニュースを朝見かけた。エージェントにWeb操作を任せる話は、もうブラウザ自動化のライブラリ選びだけでは済まない。普段のChromeプロファイルを渡すと、指示に書いていないログイン状態まで一緒に渡る。
AIブラウザを試す前にやることは、普段のプロファイルを掃除することではない。作業用に何も入っていないプロファイルを一つ決めて、そこだけを使う。まず現状を見えるようにするPythonを置いた。
Chromeプロファイルにあるものを、開かずに数える
下のスクリプトはChromeのユーザーデータディレクトリを走査し、各プロファイルに次のファイルが存在するかだけをJSONで出す。
-
Login Data: 保存済みログイン情報用のDB -
Cookies: セッションCookie用のDB -
History: 履歴DB -
Bookmarks: ブックマーク
ファイルの中身、パスワード、Cookieの値には触れない。AIに渡してよいプロファイルを選ぶための棚卸しなので、ここで秘密情報を読みにいく必要がない。
# profile_audit.py
import argparse
import json
from pathlib import Path
ARTIFACTS = {
"ログイン情報DB": "Login Data",
"Cookie DB": "Cookies",
"閲覧履歴DB": "History",
"ブックマーク": "Bookmarks",
}
def collect_profiles(root: Path) -> list[dict[str, object]]:
profiles = []
for path in sorted(root.iterdir(), key=lambda p: (p.name != "Default", p.name)):
is_chrome_profile = path.name == "Default" or path.name.startswith("Profile ")
if not path.is_dir() or not is_chrome_profile:
continue
artifacts = [
label for label, filename in ARTIFACTS.items() if (path / filename).exists()
]
extensions = path / "Extensions"
extension_dirs = (
sum(child.is_dir() for child in extensions.iterdir())
if extensions.is_dir()
else 0
)
profiles.append(
{
"profile": path.name,
"artifacts": artifacts,
"extension_dirs": extension_dirs,
}
)
return profiles
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--user-data-dir", type=Path, required=True)
args = parser.parse_args()
root = args.user_data_dir.expanduser()
report = {
"user_data_dir": str(root),
"local_state_exists": (root / "Local State").exists(),
"profiles": collect_profiles(root),
}
print(json.dumps(report, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
macOSのGoogle Chromeなら、次で実行できる。
python3 profile_audit.py \
--user-data-dir "$HOME/Library/Application Support/Google/Chrome"
このスクリプトは、Default と Profile 1 を含む空の検証用ディレクトリで動かした。Default 側にだけログイン情報DB、Cookie、履歴を置き、Profile 1 にはブックマークだけを置いた結果がこれだった。
{
"user_data_dir": "/private/tmp/profile-audit.Lbc0sg",
"local_state_exists": true,
"profiles": [
{
"profile": "Default",
"artifacts": ["ログイン情報DB", "Cookie DB", "閲覧履歴DB"],
"extension_dirs": 1
},
{
"profile": "Profile 1",
"artifacts": ["ブックマーク"],
"extension_dirs": 0
}
]
}
一つだけ注意がある。Login Data があるから保存済みパスワードが何件ある、とまでは分からない。逆にファイルが無いから安全、とも断言できない。このスクリプトは認証状態の証明器ではなく、普段使いのプロファイルを誤って使っていないかを早く見つける道具だ。ここを読み違えると、監査のつもりで秘密情報に近づく。
Default をエージェントの実行環境にしない
普段のプロファイルでは、メール、クラウドストレージ、社内SaaS、個人の買い物サイトまで同時にログインしている。指示が「請求書を一覧にして」だけでも、そのセッションが見える範囲は小さくない。ブラウザに与える権限はプロンプトよりログイン状態で決まる。ここが今回の本題。
ここで、普段用とAI用のプロファイルを分けた流れを図にするとこうなる。
新しいプロファイルでは、最初の一回に対象サービスだけへログインする。管理者アカウントは入れない。読取専用のアカウントを作れるサービスなら、そのアカウントから始める。送信や購入が必要な仕事は、下書き作成までをブラウザ側へ任せ、最後の確定操作は人が受け持つ。この順番なら、あとでプロファイルを捨てる判断も簡単になる。
拡張機能も盲点だった。Extensions の件数が多いプロファイルは、便利な反面、ページを読んだり書き換えたりする主体が増える。全件を削除する話ではない。AI用プロファイルに必要なものだけ入っているか、件数を見て一度立ち止まればいい。自分は普段の Default に入っている数を見て、そのまま渡す気がなくなった。
WebMCPの確認画面より前に、ログイン状態を狭くする
CloudflareのBrowser Renderingには、WebMCPを使ってブラウザ機能をエージェントへ公開する仕組みがある。予約完了のような操作で確認を挟めるのは有用だ。ただ、確認画面が守るのは主に最後の操作で、すでに読み込んだページやセッションCookieの範囲まで縮めてはくれない。
先にプロファイルを分けると、確認画面で見る内容も単純になる。何を送るかだけ確認すればよくなるからだ。普段のChromeに残ったメールや決済サービスを意識しながら承認するより、ずっと現実的だった。
KitesurfのようなAIブラウザが広がるほど、ブラウザプロファイルは個人設定ではなく実行権限になる。導入前に一度このJSONを取り、AI用プロファイルには不要な認証状態を持ち込まない。コードは短いが、エージェントに渡す範囲を具体的に決められる。そこから始める方が、後で大きな権限を剥がすより軽い。