この章で分かること: APIキーやパスワードを安全に扱うための原則と、暗号化・ハッシュ化・鍵を持たない設計の使い分けが分かる
聴く台本
この章で覚えてほしい結論を先に言うね。秘密情報には「コードに書かない」「漏れたら作り直す」「戻せるものと戻せないものを使い分ける」「持つべきでない鍵は持たない」という四つの原則がある。順番に話していくよ。
まず一番大事なのが、APIキーやパスワード、トークンといった秘密をソースコードに直接書かない、ということなんだ。なぜダメかというと、コードはgitで管理されるよね。gitっていうのは変更履歴を全部残す仕組みだから、一度書いて後で消しても、過去のコミットに永久に残り続ける。つまり「あ、間違えてキーを書いちゃった」と気づいて削除しても、履歴を辿れば誰でも見つけられる。しかもそのリポジトリをGitHubにpushしていたら、世界中からアクセスできる状態になる。実際、GitHubに上がったAWSのキーは数分でbotに発見されて、勝手に高額なサーバーを立てられる、なんて事故が日常的に起きている。だからキーは最初からコードに書かない。これが鉄則なんだ。
じゃあどこに書くか。答えは環境変数か、シークレットマネージャだよ。環境変数っていうのは、プログラムの外側、つまり実行する環境のほうに値を持たせておく仕組み。コードの中には「環境変数からこのキーを読む」とだけ書いて、実際の値はコードの外、たとえば.envというファイルに置く。そしてその.envファイルはgitの管理対象から外す。具体的には.gitignoreという設定ファイルに.envと書いておけば、gitはそのファイルを無視してくれる。だから履歴に刻まれない。もっと本格的にやるなら、AWSのSecrets ManagerやGoogleのSecret Managerといった専用の金庫サービスを使う。これらは値を暗号化して保管して、誰がいつアクセスしたかのログも残してくれる。003のAI導入支援事業みたいに、他社の機密データを預かるビジネスでは、こういう監査ログが取れる仕組みが信頼の土台になるんだ。
次の原則。もし秘密が漏れてしまったら、すぐにrotateする。rotateっていうのは再発行のことね。古いキーを無効にして、新しいキーを作り直す。ここで「いや、削除すればいいんじゃないの」と思うかもしれないけど、さっき言ったとおり、一度外に出た値は取り消せない。漏れたキー自体を世の中から消すことは不可能なんだ。できるのは「そのキーをもう使えなくする」ことだけ。だから漏洩を疑った瞬間に、まず古いキーを殺して新しいキーに差し替える。チャットのログにうっかり貼っちゃった、スクショに映ってた、そういう時も同じ。値が一度でも自分の管理外に出たら、それはもう漏れたものとして扱って即rotate。これを習慣にしておくと事故が小さく済む。
ここまでが「秘密をどう保管するか」の話。後半は「秘密をどう変換するか」、つまり暗号化とハッシュ化の話に移るよ。この二つはよく混同されるけど、まったく別物なんだ。
暗号化っていうのは、可逆、つまり元に戻せる変換のこと。鍵を使ってデータをぐちゃぐちゃの形に変えて、同じ鍵を使えば元のデータにきれいに戻せる。鍵を持っている人だけが中身を読める、という仕組みだね。一方でハッシュ化は、不可逆、つまり元に戻せない変換なんだ。データを一方向に潰してしまって、どんな鍵を使っても元には戻らない。同じ入力からは必ず同じ結果が出るけど、結果から入力を逆算することはできない。
で、何を暗号化して、何をハッシュするか。ここが実装の肝なんだ。判断基準はシンプルで、「あとで元の値が必要かどうか」で決める。たとえばユーザーのメールアドレスや電話番号は、メールを送ったり表示したりするために元の値が必要だよね。だからこれは暗号化する。鍵で守りつつ、必要な時に復号して使う。一方でパスワードはどうか。パスワードはハッシュ化するのが正解。なぜなら、システム側がユーザーの生のパスワードを知る必要なんて一度もないから。ユーザーがログインする時は、入力されたパスワードを同じ方法でハッシュして、保存してあるハッシュと一致するか比べるだけ。元の値は要らない。だからわざと戻せない形にしておく。こうしておけば、万が一データベースが盗まれても、攻撃者はハッシュの羅列を手に入れるだけで、生のパスワードはわからない。これがパスワードを平文で保存しちゃいけない理由であり、暗号化じゃなくてハッシュにする理由なんだ。ただし、ここで前の章の念押しを一個だけ重ねておく。パスワードのハッシュは、ただのSHAで一回潰すんじゃなくて、利用者ごとに違うソルトを足して、bcryptやargon2みたいな、わざと重く作られた専用の道具でやる。これだけは外さない。
さらにもう一段深い原則がある。それが「鍵を持つ者が復号できる、だから鍵を持たない設計にする」という考え方だよ。暗号化はたしかに強力だけど、よく考えると、復号の鍵を持っている人は中身を全部読める。だから自分のサーバーで暗号化して自分のサーバーに鍵を置いていたら、その鍵を管理する自分自身は中身を見られてしまう。サーバーが侵入されれば、鍵もデータも一緒に盗まれる。じゃあどうするか。本当に守りたいデータは、鍵を自分が持たない場所で暗号化する。これがクライアント側暗号化、つまりユーザーのブラウザの中だけで暗号化して、復号の鍵もサーバーには一切渡さない、という設計なんだ。
これが003事業のセキュリティ方針の核心でもある。お客さんの実名や電話番号みたいな個人情報は、お客さんのブラウザの中で暗号化してから預かる。復号の鍵はこちら側、つまりベンダーには渡さない。だから仮にうちのサーバーが丸ごと盗まれても、暗号化されたデータは読めない。「見ない」と約束するんじゃなくて、「構造的に見られない」状態を作る。約束は破れるけど、鍵がそもそも手元にないという事実は破れないよね。お客さんに信頼してもらうには、この「持っていないことの証明」がものすごく効くんだ。
そして最後にもう一つ、最小開示という原則を加えたい。これは「必要な分だけしか渡さない、見せない」という考え方。たとえばAIに何かを処理させる時、お客さんの個人情報をそのままAIに送る必要があるかを、毎回問い直す。名前や電話番号がAIの判断に要らないなら、送る前にマスキング、つまり伏せ字にしてしまう。「田中太郎さん」を「顧客Aさん」に置き換えてから送れば、AIは仕事をこなせるのに、個人情報は外に出ていかない。003みたいに他社の機密をAIに渡して処理するビジネスでは、このAI送信前のマスキングが命綱になる。一度AIの事業者側に渡ったデータがどこでどう使われるか、こちらは完全には制御できないから、そもそも渡さないのが一番安全なんだ。
で、実装ではどうするか。まずキーは全部環境変数かシークレットマネージャから読む。コードには値を一文字も書かない。.envは必ず.gitignoreに入れて、代わりに.env.exampleという空の雛形だけをgitに置く。漏れを疑ったら考える前にまずrotate。パスワードはハッシュ、戻す必要がある個人情報は暗号化、と種類で分ける。本気で守るデータはクライアント側で暗号化して鍵をサーバーに置かない。そしてAIや外部サービスに渡す前には、要らない個人情報をマスキングで落とす。この手順を体に染み込ませておけば、秘密情報まわりの事故はほとんど防げるよ。
この章のまとめ(実装で守る一線)
秘密は絶対にコードに書かず、環境変数かシークレットマネージャから読む。.envは.gitignoreに入れ、漏洩を疑ったら削除ではなく即rotateする。元に戻す必要がある個人情報は暗号化、戻す必要がないパスワードはハッシュ化(ソルト+bcrypt/argon2)と使い分ける。本当に守りたいデータは鍵を自分が持たないクライアント側暗号化にし、AIや外部に渡す前は個人情報をマスキングして最小限しか開示しない。