
生成AIに書かせたコードのセキュリティ対策:レビューをどう設計するか
目次
AIに書かせたコードの安全は、「気をつける」では守れません。
AIに任せると、コードは人間の数倍の速さで増えます。増えれば、穴も同じ速さで増えていきます。権限を閉じ忘れたり、秘密をコードに残したまま忘れたり、入力チェックが抜けたりします。このような穴を、1人が目視で拾いきるのは無理があります。速さを取った以上、目視はもう追いつきません。
そこで、守りを「注意力」から「仕組み」へ移しました。壁を1枚だけ厚くするのではなく、性質の違う網を何枚も重ねる、いわゆる多層防御です。PentaTrailで実際に動いている層を、外から順に、「何をして、何を防ぐか」で開けていきます。
① 境界:コードが1行動く前に落とす
いちばん外側はエッジ(Cloudflare)です。アプリに届く前に、Botや不審な自動アクセスをふるい落とします。狙うのは、脆弱性を探して回る無差別スキャンや、自動化された大量アクセスです。このようなアクセスは、こちらのコードが1行も動かないうちに、大半がここで消えます。
会員登録フォームにはTurnstileを噛ませています。Botによる大量アカウント作成やフォームの自動濫用を、裏で弾くためです。穴を塞ぐより前に、怪しいものはそもそも入れません。攻撃の的を減らすのが、最初の仕事です。
② 認証は、自前で作らない
認証まわりは、いちばん事故が起きやすい場所です。だから、中核は自分で実装せず、Supabaseに委譲して、自前のコードを最小にしています。何を任せて、何を防いでいるのかを見ていきます。
- passkey(パスワードレス):公開鍵暗号で、鍵がドメインに縛られます。偽サイトに誘導されても、そこには鍵を渡せません。フィッシングが構造的に効かない仕組みです。そもそもパスワードが無いので、漏洩も使い回し被害も起きません。
- Googleログイン(OAuth):本人確認をGoogleに預けます。こちらはパスワードを持たず、Google側の保護(不正ログイン検知や二要素認証)を、そのまま借ります。
- セッションとトークンの検証も委譲:自前実装にありがちなセッション固定やトークン偽造のミスを、作り込む余地ごと無くします。
認証だけは、AIに一から書かせません。これは量産時代の、いちばん大事な線引きのひとつでした。
③ データの境界:RLSと、関数の掟
認証を抜けられても、その先にはデータの境界があります。
土台は行レベルセキュリティ(RLS)です。テーブルの一行ごとに持ち主を縛り、各テナントが自分のデータしか見られないようにします。防ぐのはテナント越境、つまり他社のデータが見えてしまう事故です。仮にAPIの一段上で権限チェックを書き忘れても、データベースの層で、その行は持ち主にしか返りません。守りをいちばん深いところに置く、という考え方です。だからテーブルを作るその瞬間に有効化する、と規約で決めています。
関数には掟があります。強い権限で動く関数(SECURITY DEFINER)は、RLSを越えて触れるぶん、油断すると穴になります。やっかいなのは初期状態で、新しく作った関数やテーブルは「ログインしていれば誰でも触れる」設定になりがちです。データベースが、新しいオブジェクトの権限を、ログイン済みユーザー全員へ勝手に配ってしまいます。放っておくと、一般ユーザーが、本来呼べないはずの強権限の関数を直接たたけてしまいます(権限昇格)。 ここは規律で締めます。既定で開く権限は明示的に閉じ、関数の探索パスを固定し(悪意あるスキーマを差し込んで強権限を乗っ取る攻撃を防ぐため)、最小権限を保ちます。
顧客向けの口は、お客様が承認した外部ツールから MCP で繋ぐ形にしています。手元に置く資格情報が無いので、鍵の紛失や使い回しという事故がそもそも起こりません。承認は管理画面からいつでも取り消せて、呼び出しには上限をかけ、総当たりや濫用を抑えます。いちばん強い鍵、つまりRLSごと迂回できる管理者権限のキーは、フロントエンドに絶対に置きません。ブラウザに最強の鍵が露出して全データが流出する、という事故を、構造として起こせなくしています。
④ コードそのもの:書く前から安全に倒す
守りは、コードを書く前から始めています。書く前に決めている規約は、たとえばこうです。
- 入力はホワイトリストで検証し、問い合わせはパラメータ化します:SQLインジェクションや不正な値の混入を防ぐためです。
- 画面に出す値はエスケープします:クロスサイトスクリプティング(XSS)を防ぐためです。
- secretはコードに書かず、鍵の保管庫(Key Vault)に置きます:ソースが万一漏れても、鍵までは漏れないようにするためです。
- ファイルのアップロードは、拡張子と中身(MIME)を二重に確かめます:偽装したファイルの混入を防ぐためです。
これらをAIに毎回読ませる規約にしておきます。後から直すのではなく、書く前から安全側に倒しておくのが、シフトレフトの考え方です。コードを書いている最中にもセキュリティ点検が裏で走り、危ない書き方が入り込もうとした瞬間に止めます。気づくのが早いほど、直すのは安く済みます。
⑤ CIが門番:機械の検査を束で通す
書いたものは、必ずCIの門を通ります。PRを出すたびに、たくさんの検査が自動で走ります。ここが量産時代のいちばんの要です。何を検査して、何を防いでいるかを挙げます。
- 秘密情報スキャン(gitleaks):鍵やトークンの誤コミットを止めます。漏れた鍵による侵入を防ぐためです。
- 権限ゲート:強権限の関数や匿名アクセスに、開いてはいけない穴がないかを検査します。権限昇格や匿名アクセスの穴がマージされるのを、手前で止めます。
- 権限テスト(pgTAP・権限マトリクス):「この機能はログイン顧客の権限では実行できない」をテストで固定します。一度塞いだ穴が、後の変更で再び開く"退行"を防ぐためです。
- 結合テスト:APIが認証・権限を含めて決めたとおりに振る舞うかを検証します。認可ロジックの取り違えを防ぐためです。
- 型・ビルド・lint:規約からの逸脱を止めます。
- 依存追加ゲート:依存を勝手には増やさせません。供給網攻撃、つまり公開直後の悪意ある版を自動更新で引き込む事故を防ぐためです。
緑が揃うまでマージできません。gitleaksも、この束の中の一つにすぎません。人が「たぶん大丈夫」と思っても、機械が赤を出せば、そこで止まります。
CIが見張っているのは、コードだけではありません。情報セキュリティの管理体系、ISO 27001ベースのISMSそのものも、CIに組み込んでいます。統制とその証跡を文書(唯一の正)として持ち、統制と実装が乖離していく"コンプライアンスのドリフト"を、機械が見張って止めます。セキュリティの決まりごとを、棚に置く書類ではなく、機械が見張る対象にしています。ここまで来て、ようやく「仕組みで守る」と言えます。
⑥ 二刀流の目と、到達点
最後に、人とAIの目を重ねます。
権限の閉じ忘れのような穴は、同じ系統のAIどうしだと、揃って見落とすことがあります。実際、観点を分けた複数のレビューが全員すり抜けた欠陥を、別系統のAIが拾ったことがありました(ClaudeとCodexの二刀流レビューに詳しく書いています)。系統の違う目を足して、最後は人が確かめます。
完璧な防御はありません。どんな層にも、すり抜けるものは残ります。だからこそ、一枚の壁に賭けず、層を重ねます。一つ抜けても、次で止めるためです。
これはAIに限った話ではありません。人手のレビューを中心に作られた従来のシステムにも、完璧な防御はありません。違いは、守りを「人の注意力」に置くか、「仕組み」に置くか、です。人手中心のレビューは、レビュアの集中力や経験に左右され、量が増えれば取りこぼします。ここまで挙げた層、つまり境界・認証の委譲・RLSと関数の掟・コード規約・CIの門番・二刀流は、疲れず、忘れず、PRのたびに同じ厳しさで回り続けます。
だから、AIで量産したシステムでも、この多層防御を通せば、人手のレビューを中心に作られた従来のシステムと、セキュリティの水準で遜色はありません。 機械の網は、人のように疲れも油断もしないぶん、むらなく守れます。量産の速さと守りの堅さは、両立できます。それが、この一年でたどり着いた手応えでした。
このスタックを1年かけてどう組み立てたかは マイクロSaaSの技術スタックは、1年でこう変わった、開発まわりの他の記事は dev カテゴリ にまとめています。
ここまでは、自分たちのコードをどう守っているかの話でした。同じ見方は、外にも向けられます。攻撃者から自分の会社がどう見えていて、そこにどんな穴が開いているのか。それをAIで継続的に点検する仕組みが、PentaTrailです。



