Claude CodeにWordPress運用を任せたら、パスワードが平文で112行残っていた話

AIエージェントに自分のブログ運用を任せる人が増えてきました。私自身、Claude CodeにWordPressのメンテナンス(記事の整理、REST API経由の一括操作など)を任せています。便利です。ただ、便利さの裏で、認証情報がじわじわと漏れ続ける落とし穴があることに、あるとき気づきました。

この記事は、実際に自分のサイトで起きたことの記録です。「AIにサイト運用を任せているが、セキュリティまで気が回っていない」という人に、同じ轍を踏まないでほしくて書いています。

目次

何が起きていたか

WordPressをREST API経由で操作するとき、認証にはアプリケーションパスワードを使います。私は最初、こういうコマンドをClaude Codeに実行させていました。

WP_USER='myuser' WP_APP_PASS='xxxx yyyy zzzz ...' python update_posts.py

コマンドの先頭に認証情報を書いて、環境変数として渡す。よくある書き方です。動くし、手軽です。

問題は、この書き方を繰り返しているうちに、パスワードが2箇所に蓄積していくことでした。

漏洩箇所その1:Pythonスクリプトへの直書き

作業のたびにスクリプトを書いていると、その中に認証情報を直書きしてしまうことがあります。「あとで直そう」と思ったまま残る。私の場合、最終的に67個のPythonファイルにパスワードがハードコードされたままGitも通さずローカルに散らばっていました

複数世代のパスワード(過去に変更したもの)が、ファイルごとにバラバラに埋まっている状態です。失効済みのものが多かったとはいえ、認証情報の断片がディスク上に大量に残っているのは、それ自体がリスクです。

漏洩箇所その2:Claude Codeの許可設定ファイル

これが本当の落とし穴でした。

Claude Codeには、実行してよいコマンドを記録する settings.local.json という設定ファイルがあります。ここに、実行したコマンドが許可ルールとして自動的に蓄積されていく仕組みになっています。

つまり、さきほどの

WP_USER='myuser' WP_APP_PASS='xxxx ...' python update_posts.py

というコマンドを実行するたびに、パスワードを含んだコマンド文字列がそのまま許可リストに記録されていたのです。

気づいたときには、この1ファイルの中に、過去4世代分のパスワード(現行の有効なものを含む)が平文で残っていました。行数にして112行。自分では一度もパスワードを「保存」した覚えはありません。ただコマンドを実行していただけです。それが自動記録の仕組みによって、静かに溜まっていました。

なぜ気づけたのか

きっかけは、このプロジェクトをGit管理下に置こうとしたことでした。

バージョン管理を始める前に「機密ファイルがコミットに混入しないように」と.gitignoreを整備していく過程で、ファイルを一つずつ点検しました。そのときsettings.local.jsonの中身を開いて、はじめて平文パスワードの山に気づいたのです。

Git化しようとしなければ、おそらく気づかないまま運用を続けていました。AIエージェントに作業を任せるほど、自分が生成物の中身を見なくなる——この構造こそが、いちばん怖い部分だと思います。

どう直したか

対症療法と根本治療を分けて対応しました。

対症療法:漏れた分の除去

まず、既に漏れているものを消します。

  • 67ファイルのハードコードを、すべて環境変数読み込み(os.environ)に置換
  • settings.local.jsonから、パスワードを含む112行を削除(認証情報を含まない正常な許可ルールは残す)
  • 漏れていたパスワードを失効させ、新しいものに変更(ローテーション)

ここで重要なのは、消すだけでは再発するということです。同じ運用を続ける限り、次のコマンドでまた記録されます。イタチごっこになります。

根本治療:そもそもコマンドにパスワードを書かない

再発を止めるには、運用そのものを変える必要があります。ポイントは一つ。

パスワードをコマンドに書かない。環境変数はOSに永続設定しておき、コマンドは python script.py だけにする。

Windowsなら、ユーザー環境変数として永続化します。PowerShellの場合はこうです。

[Environment]::SetEnvironmentVariable("WP_USER", "myuser", "User")
[Environment]::SetEnvironmentVariable("WP_APP_PASS", "新しいパスワード", "User")

コマンドプロンプト(cmd)ならsetxでも同じことができます。

setx WP_USER "myuser"
setx WP_APP_PASS "新しいパスワード"

こうしておけば、スクリプト側は環境変数を読むだけで済みます。

import os
user = os.environ["WP_USER"]
password = os.environ["WP_APP_PASS"]

実行するコマンドは python script.py のみ。パスワードはコマンド文字列に一切登場しないので、settings.local.jsonにも記録されません。これで蓄積の蛇口が閉まります。

AIエージェントに設定させない

もう一つの工夫があります。この環境変数の設定コマンド自体を、AIエージェントに実行させないことです。

なぜなら、AIにsetx WP_APP_PASS "..."を実行させると、そのコマンドがまた許可リストに記録され、パスワードが逆戻りするからです。設定は自分の手で(Claude Codeなら!プレフィックスでユーザー自身が実行する形で)行い、AIにはパスワードの値を一切渡さないのが安全です。

ハマったところ:環境変数が引き継がれない

一つ、地味にハマった点を共有します。

環境変数を永続設定しても、すでに起動しているClaude Codeのセッションには反映されません。環境変数はプロセス起動時に親から子へコピーされる仕組みなので、設定より前に立ち上がっていたプロセスは古い状態のままです。

私は最初、Claude Codeを一度抜けて入り直せば読めると思っていましたが、それでは不十分でした。読めなかったのです。

解決には、Claude Codeを動かしている土台(ターミナルやエディタ)ごと完全に終了して、開き直す必要がありました。新しいターミナルウィンドウで環境変数が読めることを先に確認し、そのうえでClaude Codeを起動し直したら、ようやく引き継がれました。

もしそれでも引き継がれない環境なら、.envファイルに認証情報を書いてpython-dotenvで読み込む方式に切り替えるのが確実です。この場合も.envは必ず.gitignoreで除外します。

再発を防ぐルールを文書化する

最後に、同じことを繰り返さないために、AIエージェント向けの運用ルールをプロジェクトに文書として置きました。Claude CodeならCLAUDE.mdがこれにあたります。最低限、次の3つを明記しています。

  • 認証情報はコードやコマンドに直書きしない。必ず環境変数から読む
  • 破壊的・大量の操作の前にバックアップと確認を取る
  • 機密ファイル(認証情報・DBダンプ・内部データ)は.gitignoreで除外する

ルールを書いておくと、AIエージェントは作業のたびにそれを参照します。人間が毎回指示し直す必要がなくなり、運用が安定します。

まとめ:AIに任せるほど、中身を見なくなる

今回いちばんの学びは、技術的な対策そのものよりも、**「AIエージェントに作業を任せるほど、自分が生成物やログの中身を見なくなり、その死角に認証情報が溜まっていく」**という構造に気づけたことでした。

便利さと引き換えに、点検の目が減る。だからこそ、

  • 認証情報はコマンドに書かず、環境変数(または.env)で管理する
  • AIの設定ファイルやログに何が記録されているか、ときどき中身を見る
  • Git化して.gitignoreで機密を隔離し、コミット前に認証情報をスキャンする

この3点を、仕組みとして最初から組み込んでおくことをおすすめします。私のように112行溜めてから気づくより、ずっと楽です。


この記事は、実際に自分のブログ運用でClaude Codeを使う中で遭遇した事例をもとにしています。使用しているツールのバージョンや仕様によって挙動が異なる場合があるため、認証情報の取り扱いは各自の環境で十分に確認してください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

目次