【入門編】CurrentDb.QueryDefsのSQLプロパティを書き換えて実現する「動的SQL」のセキュリティ – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。
今回は、実務の現場でめちゃくちゃ役立つ「動的SQLの安全な作り方」についてお話しします。

「マクロの記録」から一歩進んで、自分の手で自由自在にデータを操りたい。そう思ったときにつきあたるのが、「画面の条件に合わせて、裏側のクエリのSQLを書き換えたい!」という壁です。

でも、ちょっと待ってください。ネットで見つけたコードをそのまま真似して、文字列をそのまま繋ぎ合わせる(連結する)書き方をしていませんか?
実はそれ、セキュリティ的に時限爆弾を抱えているようなものなんです。

今回は、Accessの心臓部である `CurrentDb.QueryDefs` を使って、SQLインジェクション(不正なデータ操作)の恐怖から身を守りつつ、スマートにクエリを動的書き換えするベストプラクティスを伝授します。
ここをクリアすれば、あなたのAccess開発スキルは一段とプロに近づきますよ。しっかりついてきてくださいね!

1. なぜ「文字列の直連結」はダメなのか?(恐怖のSQLインジェクション)

例えば、フォームに入力された「顧客名」でデータを絞り込むクエリを作りたいとします。
初学者がやりがちなのが、こんなコードです。

‘ 【絶対にいけない例:文字列の直連結】
Dim sql As String
Dim userInput As String
userInput = Me.txtSearchName.Text ‘ フォームの入力値

‘ ユーザーの入力をそのままSQLにくっつけてしまう
sql = “SELECT FROM T_顧客 WHERE 顧客名 = ‘” & userInput & “‘;”
CurrentDb.QueryDefs(“Q_検索”).SQL = sql

一見、うまく動くように見えますよね。
しかし、もしこの `userInput` に、悪意を持ったユーザーが `’ OR ‘1’=’1` なんて入力してきたらどうなるでしょう?

完成するSQLはこうなります:
`SELECT FROM T_顧客 WHERE 顧客名 = ” OR ‘1’=’1′;`

これ、すべての顧客データが丸見えになってしまいます。さらに悪質なSQL文なら、大切なデータが消されてしまうことだってあるんです。これが「SQLインジェクション」の恐怖です。

2. 安全な動的SQLの王道:パラメータ・クエリを使う

では、どうすれば安全に動的なSQLを実現できるのでしょうか?
答えは、「SQLの中に直接データを埋め込まず、パラメータ(枠組み)だけを用意して、後から安全な値を入れる」という方法です。

Access(Jet/ACEデータベースエンジン)では、クエリのSQL内で `Parameters` 宣言を行うか、あるいは `QueryDef` オブジェクトのパラメータ機能を活用することで、安全に値を渡すことができます。

今回は、実務で最も堅牢かつスマートとされる、「QueryDefのパラメータ機能」を使ったベストプラクティスコードを伝授します。

3. 【実践】CurrentDb.QueryDefsを使った安全な動的クエリ構築

あらかじめAccessのクエリウィンドウで `Q_売上検索` という名前のクエリの土台を作っておきます(中身は適当で構いません)。
そして、VBA側から以下のようにコードを記述します。

Sub ExecuteSafeDynamicQuery()
Dim qdf As QueryDef
Dim paramValue As String

‘ 1. フォームの入力値を取得(前後の空白をトリムして安全性を高める)
paramValue = Trim(Me.txtCustomerName.Value)

‘ 未入力チェック
If paramValue = “” then
MsgBox “検索する顧客名を入力してください。”, vbExclamation, “入力エラー”
Exit Sub
End If

On Error GoTo ErrorHandler

‘ 2. CurrentDbから対象のQueryDefオブジェクトを取得
‘ ※CurrentDbは呼び出すたびに新しいインスタンスを生成するため、変数に格納するのがプロの技です!
Set qdf = CurrentDb.QueryDefs(“Q_売上検索”)

‘ 3. クエリのSQLを安全な構造に書き換える(プレースホルダー or パラメータを使用)
‘ ここでは確実に型を安全に扱うため、抽出条件をパラメータ([?] や名前付きパラメータ)にします。
qdf.SQL = “PARAMETERS 抽出条件 Text(255); ” & _
“SELECT FROM T_売上明細 WHERE 顧客名 LIKE [抽出条件];”

‘ 4. パラメータに安全に値を代入する
‘ 値にシングルクォーテーションが含まれていても、SQLエンジンが「ただの文字列データ」として安全に処理してくれます。
qdf.Parameters(“抽出条件”).Value = “%” & paramValue & “%”

‘ 5. 必要であれば、このクエリをレコードセットとして開く、あるいはフォームのソースに設定する
‘ 例:フォームのレコードソースに指定する場合
Me.RecordSource = “Q_売上検索”

MsgBox “検索条件を安全に適用しました!”, vbInformation, “完了”

CleanUp:
‘ 6. オブジェクトの解放(メモリリークを防ぐための鉄則!)
Set qdf = Nothing
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume CleanUp
End Sub

コードのここがポイント!

1. `CurrentDb` を変数(`qdf`)に受けている理由
`CurrentDb` は呼び出すたびにAccess内部で新しいデータベースセッションを開きます。これを何度も直書きするとパフォーマンスが落ちるだけでなく、オブジェクトの参照が迷子になります。必ず変数に格納して使いましょう。
2. `PARAMETERS 抽出条件 Text(255);` の宣言
SQLの先頭で「このクエリはこういう型のパラメータを使いますよ」と宣言することで、データベースエンジンが厳密に型チェックを行います。これにより、不正なSQLコマンドの混入を完全にシャットアウトできます。
3. 適切なメモリ解放(`Set qdf = Nothing`)
VBAのオブジェクト変数は、プロシージャが終わると自動で消えてくれますが、巨大なデータベースを扱うAccessでは、自分で明示的に `Nothing` を代入してメモリを返すのが「できるエンジニア」の流儀です。

4. 現場で陥りやすい罠とエラー回避のコツ

罠そのもの:日付や数値の扱いに注意!

今回のサンプルは「文字列(Text)」でしたが、これが「日付」や「数値」の場合、文字列結合でやると ` #2023/10/01# ` のようなシャープやカンマの書き忘れで大パニックになります。
しかし、今回紹介した パラメータ・クエリの仕組み を使っていれば、VBA側で `Date` 型や `Long` 型の変数をそのまま `.Value = myDate` のように代入するだけで、Accessが勝手に型を解釈して安全にSQLに組み込んでくれます。もう書式に悩む必要はありません!

まとめ

いかがでしたか?
今回は `CurrentDb.QueryDefs` のSQLプロパティを書き換える手法と、セキュリティの要である「パラメータ化」について解説しました。

  • 文字列の直連結は、SQLインジェクションの温床になるので絶対NG
  • クエリの定義(QueryDef)を操作するときは、必ず `PARAMETERS` を活用する。
  • `CurrentDb` は変数に格納し、使い終わったらしっかりと解放する。

ここをクリアすれば、あなたの書くAccess VBAは、単に「動くだけのコード」から、「堅牢でプロフェッショナルなシステム」へと生まれ変わります。

日々の開発、ぜひ楽しんで進めてくださいね。あなたのエンジニアライフを応援しています!

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