目次
「先月の売上を教えて」と聞くだけで集計できるAI機能は便利ですが、モデルが生成したSQLを本番データベースへそのまま渡すと、どの情報へ触れられるかが分かりにくくなります。SELECTだけに制限しても、顧客の個人情報を大量に読み出したり、重い問い合わせでサービスを遅くしたりする問題は残ります。
本稿は、AIにSQLを実行させる機能や、Text-to-SQLの読み取り専用化を検討する開発者向けの恒久的な設計解説です。2026年10月24日にPostgreSQL 18とOWASPの一次資料を確認しました。新しい脆弱性ニュースではなく、以下のEC集計の構成とSQLは説明用の例です。実環境での動作・負荷試験は実施していません。
スポンサーリンク
最初はSQL文字列ではなく業務の引数を受け取る
本サイトが最初に勧めるのは、AIへ任意のSQLを出力させる方式より、用途を絞った集計ツールです。たとえばget_monthly_salesの引数を年月と集計軸に限定し、サーバーに保存したSQLを実行します。商品名や注文条件を自由文からSQLへ連結する必要はありません。
自由な質問に答えられる範囲は狭くなりますが、許可する列、期間、集計方法をレビューできます。ユーザーが求めた意味と、実際の集計定義が一致しているかも確認しやすくなります。最初から全テーブルの構造をモデルへ渡すと、利用者の業務に不要な内部情報まで入力へ含めてしまいます。
モデルが選ぶのは、用意された集計の種類と検証済みの引数だけにします。tenant_idのような組織識別子は、AIが自由に決める引数ではなく、認証済みセッションからサーバーが確定します。画面に表示されない引数でも、モデルやクライアントが差し替えられるなら信頼済みとは言えません。
SQLインジェクション対策と業務認可を分ける
OWASPのSQL Injection Prevention資料は、パラメーター化された問い合わせと最小権限を基本対策として説明しています。ただし、パラメーターを使えば、どの顧客のデータを読んでもよいという意味ではありません。問い合わせの構造と値の分離、利用者の閲覧範囲は別の検査です。
次は、サーバーが保持する固定SQLのイメージです。$1から$3はPostgreSQLのパラメーター表記で、実際には使用するドライバーのバインド機能で値を渡します。テンプレート文字列へ値を埋め込む例ではありません。ordersテーブルや列名も架空の定義です。
SELECT date_trunc('month', ordered_at) AS month,
sum(total_amount) AS sales
FROM reporting.orders
WHERE tenant_id = $1
AND ordered_at >= $2
AND ordered_at < $3
GROUP BY 1
ORDER BY 1;
サーバーでは、$1を認証済み利用者の所属へ結び付け、期間の上限、日時の型、開始と終了の順序を検証します。タイムゾーンと月の境界も業務仕様として決めます。「先月」がUTC基準なのか店舗の日本時間なのかで、結果は変わります。AIがもっともらしく説明できることと、会計上正しい集計であることは別です。
テーブル名、列名、並び順のように値のバインドで置き換えられない箇所は、許可した候補からコードが選びます。モデルから受け取った識別子を、エスケープしただけで自由に使う設計にはしません。柔軟性を増やすときも、まず必要な業務用途を追加する形で範囲を広げます。
スポンサーリンク
専用ロールはSELECT以外の権限経路も点検する
PostgreSQLのGRANT資料では、オブジェクトへの権限に加えてロール所属による権限も扱います。AI用接続に直接UPDATEを付けていなくても、所属ロールやPUBLICから権限を得る可能性があります。新しく付与した権限だけを見るのではなく、最終的に使える権限を確認します。
本サイトの提案は、通常アプリの接続とAI集計の接続を分け、必要なスキーマと対象へだけ読み取りを許可することです。全テーブルへのSELECTを最初から与えると、後で追加された機微情報のテーブルまで用途外で読める構成になり得ます。スーパーユーザーやテーブル所有者を、集計機能の簡便な接続先にしないでください。
関数の実行権限も対象です。SELECT文でも関数を呼べるため、「先頭がSELECTなら安全」という正規表現判定だけでは境界を説明できません。拡張機能や権限を持つ関数がある環境では、呼べるものと副作用を確認します。固定SQLを採用する利点は、この実行対象をレビューしやすいことにもあります。
ビューで公開列を絞る場合は、ビューの所有者とセキュリティ上の実行条件、基底テーブルへの直接アクセスを確認します。ビュー名を用意しただけで、元の表や別の経路が読めなくなるとは限りません。ログイン資格情報、ロール、スキーマ、表、ビュー、関数の関係を一枚の一覧にすると、更新時に検査を繰り返せます。
READ ONLYは追加の制約として使う
PostgreSQLのSET TRANSACTION資料はREAD ONLYで禁止されるコマンドを列挙しています。一方で、ディスクへのすべての書き込みを防ぐものではないとも説明しています。READ ONLYを設定したから任意SQLを安全に許可できる、とは言えません。
固定の集計を実行する処理では、次のようなトランザクション設定を補助的に使えます。時間の値は仮の設定例で、一般的な推奨値や計測結果ではありません。コネクションプールを使う場合は、処理全体で同じ接続を占有し、成功・失敗のどちらでも適切に終了してから返却します。
BEGIN READ ONLY;
SET LOCAL statement_timeout = '3s';
SET LOCAL lock_timeout = '500ms';
-- この接続でレビュー済みの固定SELECTを実行する
COMMIT;
PostgreSQLのクライアント設定資料で各タイムアウトの意味を確認し、既存の業務処理を妨げない値へ調整してください。失敗時にはROLLBACKして接続状態を戻します。トランザクションを開始した後で別の接続を使う実装では、意図した設定が適用されません。
スポンサーリンク
マルチテナントでは行と集計の境界を確認する
組織ごとの情報を同じ表へ保存する場合、tenant_idの条件が抜けたSQLは、書き込みをしなくても重大な情報漏えいにつながります。固定SQL内の条件と、必要に応じたデータベース側の行レベル制御を合わせて考えます。AIが「自社分だけ」と理解することに依存しない設計が必要です。
PostgreSQLのRow Security Policies資料は、通常の問い合わせに対する行の制限と、所有者・スーパーユーザー・BYPASSRLSの例外を説明しています。RLSを有効にしても、接続ロールが迂回する権限を持てば、想定した検査になりません。アプリと同じ接続条件で試すことが重要です。
共有接続で任意SQLを実行できるAIへ、組織IDを設定するSQLまで任せる構成にも注意してください。自分で変更できるセッション値を、信頼済みの利用者識別として扱うと境界が崩れます。ここでは固定ツールが現在の利用者を確定する方式を基本にし、任意SQLの高度な隔離設計をそのまま代替できるとは扱いません。
LIMITがあっても負荷と漏えいは残る
結果の件数を制限しても、集計前に大量の行を読んだり、複雑な結合を実行したりする負荷は残ります。広すぎる期間、並行実行、再試行による多重化を制限します。1回の問い合わせ時間だけでなく、利用者単位の同時実行数と、キャンセル後の処理が残っていないことも確認します。
また、返却する行が一件でも、個人名や小さな集団の機微情報を含むことがあります。集計結果だけを返す、不要な自由記述列を渡さない、細かすぎる条件で個人を絞れないようにするなど、用途に合わせた制御が必要です。「読み取り専用」という運用表示で、機密性の問題まで解決したように見せないでください。
本番DBへの負荷が許容できない場合は、集計用の複製や別データセットを検討します。ただし、複製先でも個人情報や組織境界は残ります。データの遅延、削除反映、認可変更の反映時間を確認し、「本番ではないから安全」という判断はしません。運用負担と鮮度の要求を比較して選びます。
回答へ渡す前と実行記録の両方を検査する
DBから返った値をすべてモデルへ送る必要はありません。利用者が必要とする集計と根拠の範囲だけを渡し、エラーへ含まれた内部テーブル名や接続情報をそのまま見せないようにします。ログも、実行した集計種別と利用者、所要時間、結果件数を中心にし、全行の内容を常時保存する必要性は別途判断します。
検証では、他組織のID、過大な期間、許可外の集計軸、タイムアウト、途中切断、同じ依頼の再送を試します。期待した失敗を確認する際は、AIの回答文だけでなく、実際にDBへ渡ったSQLとバインド値の由来を確認します。機密値をマスクした検証記録を使い、テストで新たな漏えいを作らないようにしてください。
関連記事
MCP連携でトークンの宛先と権限を確認する設計は接続の入口を扱っています。本稿のSQL集計ツールをMCPで提供する場合も、入口の認証と、DBで読めるデータの範囲を別々に制御します。
スポンサーリンク
参考資料
- OWASP:SQLインジェクション対策
- PostgreSQL 18:GRANT
- PostgreSQL 18:SET TRANSACTION
- PostgreSQL 18:行セキュリティ
- PostgreSQL 18:クライアント接続の既定値
情報確認日:2026年10月24日。構成とコードは説明用の提案で、実測や動作保証ではありません。本文と概念イラストの作成に生成AIを使用し、一次資料と保存内容を確認しています。








![[入門]LLMアプリ開発 --基本・LLMのしくみ・MCP・AIセキュリティ](https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/4707/9784297154707_1_29.jpg?_ex=240x240)
