72歳が「クラウド破産900万円」の記事を読んで、自分の全プロジェクトを点検したら本番サイトでヤバいもん見つけた話
きっかけはQiitaの一本の記事
今日の朝、Qiitaをみてたら背筋が冷えた。
Firebaseにデプロイしたアプリのエラーログを見てたら、見覚えのないAPIリクエストが大量に飛んでた。気づいたら13時間で約900万円の請求が来てた、という話。(参考:【Firebase×Gemini】13時間で請求額が約900万円になった話 - Qiita)
原因はシンプルで、Supabase+Next.jsのアプリでAPIキーをフロントエンドのJavaScriptにそのまま書いてた。それをbotに拾われて、勝手にAPIを叩かれた。
「フロントエンドにAPIキーを置いたらアカン」
これはエンジニアの世界では常識らしい。でもワタシは72歳のシニア起業家で、AIのアシスタント(くらさん)と一緒にコードを書いてる。コードの中身は正直よう分からへん。
この記事を読んで思た。
「自分のサイト、大丈夫か?」
確かめんと気が済まへん。こうなったらしらみつぶしに全部点検したろ、と腹を決めた。
ワタシのプロジェクト構成
参考までに、ジージ(ワタシのこと)が運営してるプロジェクトの構成を書いとく。
- 10man.shizuku.net(くらさんチャレンジ):Next.js+Coolify+Supabase
- sorenanbo.com(生活物価監視サービス):同じくNext.js+Coolify+Supabase
- gikai.shizuku.net(せんなん政治マップ):Next.js+Coolify
- hama.shizuku.net(はまアプリ):Next.js
- free.shizuku.net(CSFブロック解除ツール):Node.js+SMTP2GO
全部、さくらVPS上でCoolifyというコンテナ管理ツールを使って動かしてる。AWSやGoogle CloudのようなクラウドじゃないからAPIキーが漏れても即座に無制限課金にはならへんけど、それとAPIキー漏洩のリスクは別の話や。
点検するのは主に3つの観点。
- gitにAPIキーが混入してないか
- NEXT_PUBLIC_という変数名に秘密情報が含まれてないか
- 課金のある外部サービスに上限が設定されてるか
NEXT_PUBLIC_とは何か(重要)
Next.jsには、環境変数の命名ルールがある。
NEXT_PUBLIC_という文字で始まる変数名は、ビルド時にJavaScriptバンドルに埋め込まれてブラウザに公開される。誰でもブラウザの開発者ツールを開けば見れる状態になる。
逆にNEXT_PUBLIC_を付けない変数は、サーバーサイドのみで参照され、ブラウザには出てこない。
だからNEXT_PUBLIC_ANTHROPIC_API_KEYという変数名でAPIキーを設定したら即アウト。ANTHROPIC_API_KEYが正しい。
これを知らんかったら普通にやらかす。ワタシも確認するまで自信がなかった。
点検その1:.gitignoreの確認
gitignoreとは、Gitのバージョン管理から除外するファイルを指定するファイルのこと。.env.localにAPIキーを書いたとしても、gitignoreに書いてなかったらGitHubに上がってしまう。
くらさんに確認してもらったら、こんな結果が出た。
.env.local ✅ 除外済み
.env.development.local ✅ 除外済み
.env.test.local ✅ 除外済み
.env.production.local ✅ 除外済み
.env(ベースファイル) ❌ 記述なし
.env.development ❌ 記述なし
.env.production ❌ 記述なし
.env.local系は全部カバーされてたんやけど、サフィックスなしの.envが抜けてた。その場でくらさんに追加してもらった。
点検その2:gitログにAPIキーが混入してないか
git log --all -p | grep -E "ANTHROPIC|sk-ant|SUPABASE_SERVICE|anon_key"
4プロジェクト全て、何も検出されへんかった。クリーンや。
点検その3:NEXT_PUBLIC_に秘密情報が混入してないか
10man・sorenanboの.env.localにあるNEXT_PUBLIC_変数を全部リストアップしてもらった。
10man(くらさんチャレンジ)の結果:
NEXT_PUBLIC_SUPABASE_URL:公開前提でOKNEXT_PUBLIC_SUPABASE_ANON_KEY:公開前提でOK(RLS設定済み)
SUPABASE_SERVICE_ROLE_KEYはNEXT_PUBLIC_なしでサーバーサイドのみ。問題なし。
sorenanbo.comの結果:
同じ構成に加えてESTAT_API_KEY(e-Stat政府統計API)があったけど、これもNEXT_PUBLIC_なしで、サーバーサイドのコードのみで参照されてた。
点検その4:課金サービスの上限確認
Anthropic API 月間上限$10・$8でメール通知設定済み。今月の使用額は$2.14。
X(Twitter)API 残高$5のpay-per-use方式、自動チャージOFF。実質$5の上限として機能。
Google Cloud ¥10,000/月の予算アラート、50%・90%・100%でメール通知設定済み。過去30日¥0。
SMTP2GO(メール送信) 無料プランは月1,000通で上限到達時は送信停止(勝手に課金されない)。Coolifyの環境変数もNEXT_PUBLIC_なしで安全。
そして、ヤバいもんが出てきた
せんなん政治マップ(gikai.shizuku.net)の環境変数を確認したとき、くらさんから警告が上がった。
NEXT_PUBLIC_ADMIN_PASSWORD ⚠️ 危険
NEXT_PUBLIC_ プレフィックスが付いているため、
この値はブラウザのJSバンドルに含まれ、誰でも確認できます。
管理者パスワードが、NEXT_PUBLIC_付きで設定されてた。
ブラウザの開発者ツールを開いたら、誰でも管理者パスワードを見れる状態で本番稼働してた。泉南市の議会情報を公開してる現役のサービスや。
すぐくらさんに修正してもらった。
- 管理者認証の処理を
/api/admin/verifyというサーバーサイドのAPIルートに移す - 環境変数を
ADMIN_PASSWORD(NEXT_PUBLIC_なし)にリネーム - Coolifyでの環境変数変更→Redeploy完了
発見から約30分での対応やった。
今日の点検で分かったこと
AIと一緒にコードを書いてると、動くものが素早くできる。でも「動く」と「安全」は別の話や。
セキュリティの点検は、人間が意識的にやらな始まらへん。
AIに作ってもらったコードは、一度ちゃんと点検した方がええ。
特に確認してほしいのは3点。
ひとつ目はNEXT_PUBLIC_の付いた環境変数に、パスワードやAPIキーが入ってないか。ふたつ目は.gitignoreに.env系のファイルが全部入ってるか。みっつ目は課金のある外部APIに上限が設定されてるか。
72歳のジージでも、くらさんと一緒にやったら半日で全プロジェクトの点検ができた。やってみる価値は絶対ある。
次回予告
X(@bitbit_ojin)でも随時発信してるので、よかったらフォローしてな。