【実務・中級編】ADOの接続文字列を暗号化する:ソースコード内のパスワード流出を防ぐセキュリティ対策 – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

「VBAプロジェクトのパスワード」に頼るな。接続文字列を秘匿し、プロフェッショナルなセキュリティ設計を実装する。

業務自動化の現場において、Access VBAは強力な武器だ。しかし、その武器が「脆弱な設計」という錆に覆われていれば、それは単なる時限爆弾に過ぎない。

多くの開発者が陥る致命的なミスがある。それは、SQL Serverなどの外部データベースへ接続する際、接続文字列(ConnectionString)にユーザーIDやパスワードを直接記述(ハードコーディング)してしまうことだ。

「VBAプロジェクトにパスワードをかけているから大丈夫だ」と考えているなら、今すぐその考えを捨ててほしい。VBAのプロジェクトパスワードは、市販のツールを使えば数秒で突破される。ソースコード内に認証情報が残っている限り、あなたのアプリケーションは「設計段階で敗北している」と言わざるを得ない。

本記事では、プロフェッショナルなエンジニアが実践すべき、「接続情報の分離」と「マシン紐付け型暗号化」による堅牢な接続管理アーキテクチャを伝授する。

1. なぜ「ハードコーディング」は悪なのか:攻撃者の視点

エンジニアが設計を考える際、常に「攻撃者の視点」を持つ必要がある。ハードコーディングされた接続文字列には、以下のリスクが潜んでいる。

1. 静的解析への脆弱性: バイナリを解析したり、VBAプロジェクトを無理やり展開したりすれば、平文のパスワードが即座に露呈する。
2. 保守性の欠如: サーバーのパスワード変更時、全クライアントのAccessファイルを修正・再配布しなければならない。これは運用コストの爆発を意味する。
3. 情報の拡散: ソースコード管理システム(Git等)に認証情報がコミットされ、開発者全員、あるいはそれ以上の人間がパスワードを知り得る状態になる。

我々が目指すべきは、「コード(ロジック)」と「秘匿情報(シークレット)」を完全に分離(Decoupling)した設計である。

2. 採用すべきアーキテクチャ:マシン紐付け型暗号化ファイル

単に外部ファイルにパスワードを書いておくだけでは不十分だ。そのファイル自体が盗まれたら終わりだからだ。

私が推奨するのは、「特定の端末でしか復号できない暗号化設定ファイル」を用いる手法だ。具体的には以下のステップを踏む。

1. シークレットの生成: 接続文字列を生成する。
2. マシン固有キーの抽出: コンピュータ名やCPU IDなど、その端末固有の情報を取り出す。
3. 暗号化: 「接続文字列 ⊕ マシン固有キー」のような形で暗号化し、外部ファイル(.iniや.dat)に保存する。
4. 実行時復号: アプリ起動時、マシン固有キーを用いてメモリ上でのみ復号し、ADO接続に渡す。

これにより、設定ファイルが他のPCにコピーされても、パスワードを復号することは不可能になる。

3. 実装:プロダクション品質のセキュア・モジュール

それでは、実際にそのままプロジェクトに組み込めるレベルのコードを示そう。ここでは、実装の簡潔さと実用性を両立させるため、Windows APIを活用した暗号化ロジックの概念をVBAで具現化する。

3.1 構成要素

  • `modSecurity`: 暗号化・復号・マシン情報の取得を担当するコアロジック。
  • `modDatabase`: ADO接続を管理する。
  • `cfg_secret.dat`: 暗号化された接続文字列が格納されるファイル。

3.2 【重要】セキュリティ・エンジン (`modSecurity`)

このモジュールは、単なる暗号化ではなく「マシンに紐付ける」ことに主眼を置いている。

Option Explicit

' --- セキュリティ・エンジンの定義 ---
' 開発者への警告: このモジュールは、暗号化アルゴリズムの変更が必要な場合、
' 全ての既存設定ファイルを再生成する必要がある。

Private Const SALT_KEY As String = "A7x!9pQ#zL2m" ' ソルト(鍵を強化するための文字列)

''' <summary>
''' コンピュータ名に基づいたマシン固有のキーを生成する
''' </summary>
Public Function GetMachineKey() As String
    GetMachineKey = Environ("COMPUTERNAME") & SALT_KEY
End Function

''' <summary>
''' 文字列をマシン固有キーで暗号化する (XOR演算による簡易実装)
''' 本来はAES等の強力なアルゴリズムを推奨するが、VBA内での完結性と
''' 「ファイル流出時の無効化」を優先した設計とする。
''' </summary>
Public Function EncryptString(ByVal plainText As String, ByVal key As String) As String
    Dim i As Long, j As Long
    Dim encrypted As String
    Dim keyLen As Long: keyLen = Len(key)
    
    For i = 1 To Len(plainText)
        ' 文字コードをキーでXOR演算し、文字化けを防ぐためBase64的な処理を行うのが理想だが、
        ' ここでは実装の明快さのため、文字コード変換による手法を用いる
        j = ((i - 1) Mod keyLen) + 1
        encrypted = encrypted & Chr(Asc(Mid(plainText, i, 1)) Xor Asc(Mid(key, j, 1)))
    Next i
    EncryptString = encrypted
End Function

''' <summary>
''' 暗号化された文字列を復号する
''' </summary>
Public Function DecryptString(ByVal cipherText As String, ByVal key As String) As String
    ' XOR演算の特性(A ⊕ B ⊕ B = A)を利用して復号
    DecryptString = EncryptString(cipherText, key)
End Function

''' <summary>
''' 設定ファイルを保存する
''' </summary>
Public Sub SaveSecretFile(ByVal connectionString As String, ByVal filePath As String)
    Dim encrypted As String
    encrypted = EncryptString(connectionString, GetMachineKey())
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Dim ts As Object
    Set ts = fso.CreateTextFile(filePath, True)
    ts.Write encrypted
    ts.Close
    
    MsgBox "接続情報の暗号化保存が完了しました。", vbInformation
End Sub

''' <summary>
''' 設定ファイルから復号した接続文字列を読み込む
''' </summary>
Public Function LoadSecretConnectionString(ByVal filePath As String) As String
    On Error GoTo ErrHandler
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    If Not fso.FileExists(filePath) Then
        Err.Raise 5, , "設定ファイルが見つかりません。"
    End If
    
    Dim ts As Object
    Set ts = fso.OpenTextFile(filePath, 1)
    Dim encryptedContent As String
    encryptedContent = ts.ReadAll
    ts.Close
    
    LoadSecretConnectionString = DecryptString(encryptedContent, GetMachineKey())
    Exit Function

ErrHandler:
    LoadSecretConnectionString = ""
    MsgBox "認証情報の読み込みに失敗しました。端末が異なるか、ファイルが破損しています。", vbCritical
End Function

3.3 データベース接続管理 (`modDatabase`)

接続文字列のライフサイクルを管理し、ADOオブジェクトを適切に制御する。

Option Explicit

' ADOの参照設定が必要 (Microsoft ActiveX Data Objects x.x Library)
Private pConn As ADODB.Connection

''' <summary>
''' セキュアな接続文字列を用いてデータベースに接続する
''' </summary>
Public Function ConnectToDatabase(ByVal configFilePath As String) As Boolean
    On Error GoTo ErrHandler
    
    Dim connStr As String
    connStr = LoadSecretConnectionString(configFilePath)
    
    If connStr = "" Then
        ConnectToDatabase = False
        Exit Function
    End If
    
    Set pConn = New ADODB.Connection
    ' タイムアウト設定など、実務的なパラメータをここで制御
    pConn.ConnectionTimeout = 15
    pConn.Open connStr
    
    ConnectToDatabase = True
    Exit Function

ErrHandler:
    Debug.Print "Connection Error: " & Err.Description
    ConnectToDatabase = False
End Function

''' <summary>
''' 接続を安全に解除する
''' </summary>
Public Sub DisconnectDatabase()
    If Not pConn Is Nothing Then
        If pConn.State = adStateOpen Then pConn.Close
        Set pConn = Nothing
    End If
End Sub

' 外部から接続オブジェクトを利用するためのプロパティ
Public Property Get Connection() As ADODB.Connection
    Set Connection = pConn
End Property

3.4 初期設定ツール(開発時のみ使用)

このコードは、開発者が自身のPCで一度だけ実行し、暗号化ファイルを作成するためのものだ。

Sub SetupInitialConfig()
    Dim rawConnStr As String
    Dim targetFile As String
    
    ' 本来は入力ダイアログ等から取得すべき
    rawConnStr = "Provider=SQLOLEDB;Data Source=SERVER_NAME;Initial Catalog=DB_NAME;User ID=myUser;Password=myPassword;"
    targetFile = CurrentProject.Path & "\cfg_secret.dat"
    
    SaveSecretFile rawConnStr, targetFile
End Sub

4. アーキテクトの視点:実務における注意点

この実装をプロダクション環境へ投入する際、以下の3点に留意せよ。

① エラーハンドリングの「情報の粒度」

接続に失敗した際、`Err.Description` をそのままユーザーに表示してはならない。エラーメッセージの中に「パスワードが違います」といった情報が含まれている場合、それが攻撃のヒントになる。
「接続に失敗しました。管理者にお問い合わせください」 という抽象的なメッセージに留め、詳細なエラーはローカルのログファイルにのみ出力するのが鉄則だ。

② オブジェクトのライフサイクル管理

ADOの `Connection` オブジェクトは、使い終わったら必ず `Close` し、`Nothing` を代入してメモリから解放せよ。特に、ループ内で接続・切断を繰り返すような設計は、リソースリークとパフォーマンス低下の元凶となる。

③ バックアップと展開の戦略

マシン紐付け型にした場合、PCをリプレイスした瞬間にアプリは動かなくなる。
運用設計として、「新しいPCに対して設定ファイルを再生成するための専用ツール」を、管理者権限を持つユーザーのみが実行できる形で用意しておく必要がある。

結論

「動けばいい」という考えで作られたツールは、いつか必ずセキュリティという名の壁にぶち当たる。
接続文字列をコードから剥がし、マシン固有のキーで保護する。この一歩が、単なる「作業の自動化」を、「信頼される業務システム」へと昇華させるのだ。

設計の美しさは、コードの短さではなく、その堅牢さと保守性のバランスにあることを忘れないでほしい。

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