このレッスンで終わる頃には
- なぜAPIキーをチャットに貼ってはいけないのか、腹落ちしてる
- 「Claudeに見せずに使わせる」型を1つ覚えている
- 自分の過去のClaude Codeに、漏れがないかチェックできる
正直に言うと、私はずっと貼ってきた
新しいAPIサービスのキーを取得すると、そのままClaudeとの会話に貼って、コードを書いてもらって、動かす。
OpenAIで画像生成したいとき。Geminiで翻訳させたいとき。Stripeのテストをしたいとき。
これ、多くの人が無自覚にやってます。私もそうでした。
そしてある日気づきました。チャットに貼った瞬間、そのキーは"ずっと残る" ことに。
何が起きるか
APIキーを1回チャットに貼ると、こうなります。
- ローカルの会話ログ(
~/.claude/projects/)に永久保存される - Anthropic側のサーバーログにも記録される(規約上、品質改善のため)
- 過去の会話を引き継いで再開すると、コンテキストとして再ロードされる
つまり、コードリポジトリに間違ってcommitしたキーと同じ重さで扱う必要があります。
「自分のローカルにしかないから大丈夫」じゃない。漏れたものとして対処すべき。
私の例:1日のヒヤリハット
ある日、Claudeに「OPENAI_API_KEYが設定されているか確認して」と頼みました。
書かれたコマンドはこれ。
echo "API key set: ${OPENAI_API_KEY:+yes}${OPENAI_API_KEY:-no}"
実行した瞬間、ターミナルに 164文字のキー全文 が表示されました。
${VAR:-default} は変数があれば default ではなく値そのものを返す仕様。「設定済みならyes、未設定ならno」のつもりが、「設定済みなら値全部、未設定ならno」になってた。
画面に出した瞬間、もう手遅れ。会話ログに残ってます。
そこから過去のClaude Code履歴を全部スキャンしたら、現役のGemini APIキーまで過去の会話に残っていました。「漏れてない」と思ってたものが、自分の不注意で漏れてた。
安全な型を1つだけ覚える
これからは、原則を1つに絞ります。
APIキーをClaudeに見せない。AIには「環境変数を使うコードを書いて」と指示する。
具体的にはこうなります。
やめた書き方:
「OpenAIで画像生成して。キーは sk-proj-xxxxx です」
新しい書き方:
「OpenAIで画像生成して。OPENAI_API_KEY は ~/.zshrc に設定済み」
差分は2行だけど、意味が全然違います。
AIが書くPythonコードは os.environ['OPENAI_API_KEY'] を読むだけなので、キーの値そのものはAIの目に触れない。
ステップ1:APIキーを ~/.zshrc に書く
ターミナルで:
open -e ~/.zshrc
開いたファイルの末尾に追記:
export OPENAI_API_KEY=sk-proj-(ここに貼り付け)
export GEMINI_API_KEY=AIzaSy(ここに貼り付け)
保存(Cmd+S)→ ターミナルで反映:
source ~/.zshrc
これで全Pythonスクリプトから os.environ['OPENAI_API_KEY'] で読める状態になります。
ステップ2:Claudeにはコードだけ書かせる
「OpenAIで画像生成するスクリプトを書いて。
APIキーは OPENAI_API_KEY という環境変数に設定済み。」
これでClaudeが書くコードはこんな感じになる:
from openai import OpenAI
client = OpenAI() # 環境変数を自動で読む
result = client.images.generate(...)
キー値はClaudeの目に入らない。会話ログにも残らない。安全。
ステップ3:環境変数の確認は値を出さないコマンドで
「ちゃんと設定されてるかな?」を確認したいとき:
# 存在確認(値は出さない)
[ -n "$OPENAI_API_KEY" ] && echo "OK" || echo "NG"
# 識別したいなら部分表示
echo "先頭8文字: ${OPENAI_API_KEY:0:8}"
echo "長さ: ${#OPENAI_API_KEY}"
echo "$OPENAI_API_KEY" も printenv OPENAI_API_KEY も、もう書かない。
自分の過去ログを棚卸ししてみる
不安なら、過去のClaude Codeログにキーが残ってないか、自分でスキャンできます。
CLAUDE_DIR="$HOME/.claude/projects"
echo "OpenAI: $(grep -rho 'sk-proj-[A-Za-z0-9_-]*' $CLAUDE_DIR 2>/dev/null | awk 'length($0)>=40' | sort -u | wc -l) ユニーク値"
echo "Anthropic: $(grep -rho 'sk-ant-[A-Za-z0-9_-]*' $CLAUDE_DIR 2>/dev/null | awk 'length($0)>=40' | sort -u | wc -l) ユニーク値"
echo "Google: $(grep -rhoE 'AIza[A-Za-z0-9_-]{30,}' $CLAUDE_DIR 2>/dev/null | sort -u | wc -l) ユニーク値"
echo "GitHub: $(grep -rhoE 'gh[ps]_[A-Za-z0-9]{30,}' $CLAUDE_DIR 2>/dev/null | sort -u | wc -l) ユニーク値"
ヒットがあれば、それは「漏れていた」キー。
ヒットしたらどうするか
該当のサービスに行ってrotate(古いキーを無効化、新しいキーを作る):
| サービス | URL |
|---|---|
| OpenAI | https://platform.openai.com/api-keys |
| Google AI (Gemini) | https://aistudio.google.com/app/apikey |
| Anthropic | https://console.anthropic.com/settings/keys |
| GitHub | https://github.com/settings/tokens |
新しいキーを ~/.zshrc に書き直して source ~/.zshrc で反映。
もう使ってないサービスなら、プロジェクトごと削除するのが一番きれい。家ごと取り壊せば鍵は意味を失います。
医療現場でも同じ構造
このパターンは、AIに患者IDや診療情報を扱わせるときも同じになります。
カルテのテキストをチャットに貼って「まとめて」と頼む運用は、APIキーを貼るのと等価で危険。
正しいのは、「カルテファイルから読み込んで処理する関数を書いて」と頼むこと。AIが書くコードがファイルを読みに行くだけで、AIの目に直接データが触れない設計にする。
開発の現場で型を整えておくと、医療データを扱うときに同じ型がそのまま使えます。
一行でまとめる
AIに渡したものは、コードに書いたのと同じ重さで扱う。
これ1つだけ覚えていれば、たぶんもう同じ事故は起きません。
次は Hooks と Skills。「また同じこと頼んでるな」を、自動と専用コマンドで肩の荷を下ろす話。
APIキー管理のベストプラクティス。環境変数、サーバー側保管、ローテーションの考え方
明日のアクション
3つやってみてください。
~/.zshrcを開いて、使っているAPIキーをexport OPENAI_API_KEY=...の形で書き込む(Claudeに直接は貼らない)source ~/.zshrcで反映、[ -n "$OPENAI_API_KEY" ] && echo OKで確認- 過去のClaude Code会話ログを
grep -rho 'sk-proj-[A-Za-z0-9_-]*' ~/.claude/projects/で棚卸し、ヒットしたら該当サービスでrotate
この3手で、過去の漏れと今後の漏れの両方が止まります。
