【実務・中級編】【上級】CurrentDb.QueryDefsのSQLプロパティを動的書き換え:SQLインジェクションを防ぐパラメータ管理 – Access VBA解析バイブル

スポンサーリンク

【Access VBA】SQLインジェクションを撲滅せよ:QueryDefパラメータによる「動的クエリ」の極意

Access開発の現場で、未だに「文字列連結」でSQLを組み立てている者はいないだろうか。

‘ 【絶対禁止】やってはいけない連結SQL
SQL = “SELECT FROM T_受注 WHERE 顧客ID = ” & Me.txtID

この書き方は、初心者には簡単に見えるかもしれない。だが、これは「セキュリティの穴」を自ら開けているに等しい。さらに言えば、クエリの実行プランがキャッシュされず、パフォーマンス上の損失も大きい。

真のエンジニアは、QueryDefオブジェクトのParametersコレクションを使いこなす。今回は、Access VBAにおける「安全かつ高速」な動的クエリ構築の極限を伝授する。

なぜ「文字列連結」は悪なのか

1. セキュリティリスク(SQLインジェクション): ユーザー入力に特殊文字(`’` や `;` など)が含まれていた場合、クエリ構造が破壊される。悪意あるコマンドを注入されれば、DB内の全データが流出・削除される危険がある。
2. 実行プランの非効率性: 文字列連結で生成されるSQLは、実行のたびに「異なるクエリ」として認識される。Accessエンジンは毎回コンパイルをやり直すため、高負荷な環境では目に見えて処理が遅延する。
3. 型変換の地獄: 日付や数値のフォーマットを自前で制御するのは、バグの温床だ。

解決策:QueryDefとParametersの正しい作法

SQLを文字列で連結するのではなく、SQLテンプレートを作成し、値をパラメータとして流し込む。これがAccessにおける正攻法だ。

実践的なプロダクションコード

以下の関数は、汎用的に使えるクエリ実行のテンプレートである。

‘ @Description: パラメータ付きクエリを実行し、レコードセットを返す堅牢な関数
‘ @Param qdfName: 既存のクエリ定義名
‘ @Param params: パラメータ値の配列
Public Function GetSecureRecordset(ByVal qdfName As String, ParamArray params() As Variant) As DAO.Recordset
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim i As Integer

Set db = CurrentDb
Set qdf = db.QueryDefs(qdfName)

‘ パラメータのクリア(キャッシュの影響を防ぐ)
‘ ※一度実行したqdfはパラメータが残るため再利用時は注意

‘ パラメータを順にバインドする
For i = LBound(params) To UBound(params)
‘ Parametersコレクションに値を代入
‘ 型変換はAccessエンジンが自動で安全に行う
qdf.Parameters(i).Value = params(i)
Next i

‘ パラメータ付きでクエリを実行
Set GetSecureRecordset = qdf.OpenRecordset(dbOpenSnapshot)

‘ オブジェクトの解放は呼び出し元で行うのが基本だが、
‘ ここではqdfを閉じてメモリを保護する
Set qdf = Nothing
End Function

使用例:フォームからの呼び出し

Private Sub btnSearch_Click()
Dim rs As DAO.Recordset

‘ 顧客ID(数値)と開始日(日付)をパラメータとして渡す
‘ クエリ側には [prm_CustomerID] と [prm_StartDate] を定義しておくこと
Set rs = GetSecureRecordset(“Q_受注検索”, Me.txtID, Me.txtDate)

‘ データ処理…

rs.Close
Set rs = Nothing
End Sub

現場で差がつく「極限の知見」

1. パラメータの宣言を怠るな

クエリ(`Q_受注検索`など)のSQLデザインビューで、必ず「パラメータ」設定を行うこと。
`PARAMETERS prm_CustomerID Long, prm_StartDate DateTime;`
これを先頭に記述することで、AccessはSQLコンパイル時に型の検証を行う。これが最速の実行速度を保証する。

2. CurrentDbを乱用するな

`CurrentDb` は呼び出すたびに新しいデータベースオブジェクトを生成する。ループ内で何度も `CurrentDb` を叩くのは自殺行為だ。
コードの冒頭で一度 `Dim db As DAO.Database: Set db = CurrentDb` と変数に格納し、そのインスタンスを使い回せ。これはメモリ管理の基本中の基本である。

3. DAO vs ADO

Accessのネイティブな操作であれば、迷わずDAOを選択せよ。ADOはSQL Server等との連携には強力だが、Accessローカルのファイル操作においてDAOの速度に勝るものはない。

まとめ:保守性の高いシステムを作るために

コードを書くとき、常に問いかけてほしい。
「このコードは、半年後の自分が仕様変更したときに、副作用なく修正できるか?」

パラメータ化されたクエリ設計は、SQLとロジックを完全に分離する。SQLの調整が必要になっても、VBAのコードを一行もいじらずにクエリ定義を書き換えるだけで済む。

「動的」とは、文字列を継ぎ接ぎすることではない。
「可変要素を安全な枠組みに流し込むこと」を指す。

この設計思想こそが、伝説のエンジニアの第一歩だ。現場のコードを、今日から「安全」で「高速」なものに書き換えろ。健闘を祈る。

タイトルとURLをコピーしました