境 耕佑

シークレットを管理する

AIコーディングが当たり前となり、サービスの開発は劇的にかんたんになりました。個人でも複数のサービスを同時に開発することができます。サービス開発ではさまざまなシークレット情報をつかいます。APIキーや秘密鍵と呼ばれるものです。私は、シークレットの多くをローカルマシンに平文で保存していました。

しかし、AIコーディングではこれが少し困った問題を引き起こします。シークレットが流出してしまうリスクがあるのです。AIが意図せずにシークレット情報を読んでしまい、コンテキストや外部サーバーにシークレットを送信してしまうことがあります。この問題を解決するべく、シークレットをすべて1Passwordに移行し、集中管理できるようにしました。

1Passwordに保存したシークレットは、AIに値を読ませず、必要なプロセスへ環境変数として渡します。これにより、AIとの会話に秘密の値を含めずに認証できます。

移行の要件は次の三つです。

  • すべてのシークレットを1Passwordで管理する : シークレットはどこでどうつかっているかすぐ忘れてしまいます。開発作業でつかうものもデプロイ先でつかうものも、シークレット情報はすべて1Passwordに集約します。
  • worktreeでの開発に対応する : worktreeはバージョン管理しているファイルしかコピーしてくれません。worktreeで開発を進めていると、シークレットを読み取れずAIが途方に暮れてしまうことがしばしばありました。そういったことを防ぎたいと考えました。
  • リモート環境で開発できる : 私は普段OrcaというADEをつかって開発しています。出先ではOrca Mobileからリモートアクセスします。1Passwordにはデスクトップアプリでの認証連携機能があるのですが、Orca Mobile環境ではデスクトップアプリでの認証はできません。そこで、デスクトップアプリ認証以外の方法で認証する必要があります。

やったこと

開発用シークレットに加え、本サイト(https://chouyo.com/)と立替精算Bot「アリーちゃん」でつかっているシークレットを例に、移行前後を見てみましょう。

移行前はシークレットを次のように保管していました。ファイル名の下には、保存している変数名を示しています。

~/
├── .ssh/
│   └── id_rsa  # 秘密鍵。GitHubへのアクセスに使用
├── .dsh/
│   └── .credentials.yaml  # 開発でつかっているDeepSeek Harnessのシークレット保存ファイル
│       └── OLLAMA_API_KEY  # コーディングでメインにつかっているOllama CloudのAPIキー
├── chouyo.com/
│   └── .env
│       ├── GSC_SA_KEY  # Google Search Consoleのサービスアカウント。アクセス分析用。
│       ├── CF_API_TOKEN  # CloudflareのAPIトークン。アクセス分析用。
│       ├── CF_ACCOUNT_ID  # CloudflareのアカウントID。アクセス分析用。
│       ├── CF_SITE_TAG  # Cloudflareのサイトタグ。アクセス分析用。
│       ├── TYPESAFE_API_KEY  # JevのAPIトークン。おすすめ記事生成用。
│       ├── BUFFER_ACCESS_TOKEN  # SNS連携のAPIトークン。記事の告知用。
│       └── CLOUDFLARE_API_TOKEN  # Cloudflare APIトークン。デプロイ用。
└── ally-chan/
    └── .env
        └── CLOUDFLARE_API_TOKEN  # Cloudflare APIトークン。デプロイ用。
        Cloudflare Workers(ローカル保存なし)
        ├── LINE_CHANNEL_SECRET  # LINE公式アカウントのチャンネルシークレット。
        ├── LINE_CHANNEL_ACCESS_TOKEN  # LINE公式アカウントのアクセストークン。
        └── TYPESAFE_API_KEY  # JevのAPIトークン。費目の分類用。

これを1Passwordに移行すると次のようになりました。vaultとは1Passwordの一般的な保管庫です。Environmentsとは1Password Environmentsのことで、.envファイルで管理していたような環境変数をまるごと管理できる機能です。

1Password
├── vault
│   └── Dev
│       ├── githubPAT
│       └── OLLAMA_API_KEY
└── Environments
    ├── chouyo.com
    │   ├── GSC_SA_KEY
    │   ├── CF_API_TOKEN
    │   ├── CF_ACCOUNT_ID
    │   ├── CF_SITE_TAG
    │   ├── TYPESAFE_API_KEY
    │   ├── BUFFER_ACCESS_TOKEN
    │   └── CLOUDFLARE_API_TOKEN
    └── アリーちゃん
        ├── CLOUDFLARE_API_TOKEN
        ├── LINE_CHANNEL_SECRET
        ├── LINE_CHANNEL_ACCESS_TOKEN
        └── TYPESAFE_API_KEY

プロセスに環境変数として渡せるシークレットは1Password Environments、個別につかうシークレットはvaultと、目的に応じて分けて管理します。今回つかうサービスアカウントではPrivate vaultにアクセスできないため、Devという保管庫を新しく作りました。1Password Environmentsではプロジェクト(リポジトリ)ごとにEnvironmentsを分けて作ります。そうすることで、関係ないシークレットが混入するリスクを減らします。

デプロイ先のサービスに保存して使用するシークレットも、原本は1Passwordで管理します。どこでどのシークレットを使用しているか一元的に管理できるようにするためです。

SSH秘密鍵は廃止しました。1PasswordにはSSH Agentという機能があります。鍵の使用にはデスクトップアプリでの承認が必要で、外出先から操作する今回の運用には合いませんでした。GitHubへのアクセスはPATをつかって、HTTPS経由で認証するようにしました。

1Passwordの認証にはサービスアカウントをつかいました。サービスアカウントとは、サービスアカウントトークンをつかうことで、あらかじめ登録してあるシークレット情報にアクセスできる仕組みです。Dev vaultとすべてのEnvironmentsへの読み取り権限を持たせたサービスアカウントをひとつ発行してつかいます。

ほかにも1PasswordにはShell Pluginsという機能があります。対応するCLIであれば、op run や op read をつかう処理を自分で書かずに、1Passwordに保存した認証情報をつかえます。GitHub CLIや、Cloudflare Workersの開発につかうWranglerに対応しています。ただし、これもデスクトップアプリでの認証が必要になるのでつかいませんでした。

事故は減らせるが攻撃には無力

すべてのシークレット情報を1Passwordで管理できました。これでAIによる流出の事故リスクを減らすことができました。しかし、大きいリスクがひとつ残っています。サービスアカウントトークンです。リモート環境から開発するためにはどうしてもローカル環境に平文で保存しなくてはなりません。ここはリスクと利便性を天秤にかけて諦めました。ここら辺は1Passwordのモバイルアプリの進化に期待したいですね。サービスアカウントの一時停止ができたり、モバイルアプリから認証の承認ができたりすると、真の意味で平文でのシークレット保存をゼロにできると思います。

問題はもうひとつあります。悪意ある攻撃への対策にはほとんどならないことです。それは別途検討する必要があります。

最後までありがとうございました