「MD5 + 固定ソルト」で保存したパスワードは、逆引きサービスに入れた瞬間に平文へ戻ります。`.env` にベタ書きした APP_KEY や AWS キーは、あとで `git rm --cached` しても履歴に残り続けます。守り方も置き場所も、"隠したつもり"が命取りになります。
社内エンジニア向けセキュリティ勉強会(全6回)の第4回です。暗号・データ保護・シークレット管理という広いテーマを「秘密には守り方(どう保管するか)と置き場所(どこに置くか)の両方が要る」という2枚看板で束ね、共通の敗因=自前実装と平文、共通の正解=検証済みのフレームワーク/マネージドサービスに委ねる、という一本の思想に収束させます。Laravel 13.x と FuelPHP 1.x/PHP 標準の両方で、危険なコードと安全なコードを並べます。
疑似デモは2つ。md5("password") のハッシュ値を逆引きに投げて一瞬で平文に戻るところ("複雑そう"な P@ssw0rd も同様)。そして .env をコミットしてから削除しても、`git show <過去コミット>:.env` に平文の鍵がそのまま残るところ。だから対策は「削除」ではなく「ローテーション」だ、という結論まで持っていきます。
扱うトピック
- 前提整理:ハッシュ(一方向・照合向け)と暗号(可逆・鍵で戻せる)は別物。「パスワードを暗号化」も「データをハッシュ」も誤り
- パスワードの守り方:MD5/SHA1 が保存に向かない理由(速い=総当たり向き/ソルト無しはレインボーテーブルで一括/衝突既知)、ソルト・ストレッチング(work factor)・専用アルゴリズムの3つの武器、OWASP 第一推奨の Argon2id と bcrypt、Laravel の Hash::make / check / needsRehash、PHP 標準の password_hash / verify / needs_rehash
- 可逆データの守り方:Laravel Crypt(AES-256-CBC + MAC)と APP_KEY による Cookie・セッションの暗号化、そして APP_KEY が漏れれば暗号は全部無効になるという急所
- 秘密の置き場所:.env は .gitignore + 共有は変数名だけの .env.example、Git の不変履歴、AWS Secrets Manager / Parameter Store への集約と使い分け、IAM 最小権限(特定 ARN に限定)、鍵ローテーション
持ち帰る3つの結論
1. パスワードは Hash::make に委ね、MD5 で保存しない
2. 可逆データは Crypt で暗号化し、APP_KEY を守る
3. 秘密はマネージドに置き、最小権限 + ローテーション
レガシー保守(FuelPHP 1.x)でも「明日からできる」棚卸しと設定確認まで示します(自前ハッシュの grep、.env の履歴確認、本番の鍵を1つマネージドへ)。Appendix には Argon2id/bcrypt の推奨パラメータ、Secrets Manager と Parameter Store の詳細比較、git 履歴から秘密を除去する手順も付けています。
想定読者:「平文じゃないから安全」と思っていた"ちょっと前の自分"。
分類:OWASP A02:2021 Cryptographic Failures、CWE-327/328(脆弱な暗号・ハッシュ)、CWE-259/798(資格情報のハードコード)、CWE-312(機密情報の平文保存)