本章では、VB.NET業務システムにおけるセキュリティ設計、認証、認可について学習します。
業務システムでは、顧客情報、社員情報、売上情報、請求情報、契約情報、個人情報など、 重要なデータを扱うことが多くあります。 そのため、アプリケーションを正常に動作させるだけでなく、 不正アクセス、不正操作、情報漏洩、改ざん、誤操作を防ぐ設計が必要です。
セキュリティは、ログイン画面だけで完結するものではありません。 入力値検証、SQLインジェクション対策、権限管理、パスワード管理、暗号化、操作ログ、 機密情報管理、ファイルアクセス制御など、複数の観点を組み合わせて設計する必要があります。
本章のゴールは、VB.NETアプリケーションに最低限のセキュリティ機能を追加することではありません。 業務システムとして、安全性、運用性、監査性を考慮したセキュリティ設計を行えるようになることです。
1. 業務システムにおけるセキュリティ設計の基本
セキュリティ設計では、「攻撃者から守る」だけでなく、 「誤操作を防ぐ」「権限外の操作を防ぐ」「問題発生時に追跡できる」ことも重要です。
特に社内業務システムでは、外部攻撃だけでなく、内部利用者による誤操作や権限設定ミスも大きなリスクになります。
業務システムで守るべきもの
- 個人情報
- 顧客情報
- 社員情報
- 売上・請求・支払情報
- 契約情報
- 認証情報
- 業務上の機密データ
- 操作履歴
- システム設定情報
代表的なセキュリティリスク
- SQLインジェクション
- 不正ログイン
- 権限外操作
- パスワード漏洩
- 機密ファイルの不正閲覧
- ログへの個人情報出力
- 設定ファイルからの接続情報漏洩
- 操作ログ不足による追跡不能
- 入力値不備によるデータ破損
セキュリティ対策は、ひとつの機能だけで完璧にするものではありません。 入力検証、認証、認可、ログ、暗号化、監査、運用ルールを組み合わせて、多層的に守ることが重要です。
2. 入力値検証
入力値検証は、セキュリティ設計の基本です。 ユーザーが入力した値、外部ファイルから読み込んだ値、APIから受け取った値などは、 すべて信用せずに検証する必要があります。
不正な入力値をそのまま処理すると、SQLインジェクション、パストラバーサル、 データ不整合、例外発生、業務ルール違反などにつながる可能性があります。
検証すべき入力値
- 画面から入力された値
- CSVやExcelから取り込んだ値
- 外部APIから受信した値
- URLやファイルパス
- 検索条件
- ログインID
- メールアドレス
- 数値項目
- 日付項目
入力チェックの種類
- 必須チェック
- 文字数チェック
- 形式チェック
- 数値チェック
- 範囲チェック
- 日付チェック
- 禁止文字チェック
- 存在チェック
- 重複チェック
- 業務ルールチェック
入力チェックの例
Public Class UserInputValidator
Public Function Validate(dto As UserRegisterDto) As List(Of String)
Dim errors As New List(Of String)()
If String.IsNullOrWhiteSpace(dto.UserName) Then
errors.Add("ユーザー名を入力してください。")
ElseIf dto.UserName.Length > 50 Then
errors.Add("ユーザー名は50文字以内で入力してください。")
End If
If String.IsNullOrWhiteSpace(dto.Email) Then
errors.Add("メールアドレスを入力してください。")
ElseIf Not dto.Email.Contains("@") Then
errors.Add("メールアドレスの形式が不正です。")
End If
Return errors
End Function
End Class
入力チェックは画面イベント内に直接書きすぎず、Validatorクラスへ分離すると保守しやすくなります。 同じチェックを画面、CSV取込、APIなどで再利用しやすくなるためです。
入力値検証の基本方針
- ユーザー入力を信用しない
- 外部システムから受け取った値も信用しない
- 可能な限り許可リスト方式で検証する
- エラー内容は利用者が修正できる形で表示する
- 検証処理を共通化する
- 検証と業務ロジックを混在させすぎない
3. SQLインジェクション対策
SQLインジェクションは、ユーザー入力を悪用してSQL文の意味を改変し、 不正なデータ取得、更新、削除などを行う攻撃です。
VB.NETの業務システムでは、画面から入力された検索条件やログイン情報をSQLに使用する場面が多くあります。 このとき、文字列連結でSQLを組み立てると非常に危険です。
危険なSQLの例
Dim sql As String =
"SELECT * FROM Users WHERE LoginId = '" & loginId & "' AND Password = '" & password & "'"
このようなSQLでは、入力値によってSQLの条件が改変される可能性があります。 また、シングルクォートを含む文字列でSQLエラーが発生することもあります。
安全なパラメータ化クエリ
Dim sql As String =
"SELECT UserId, LoginId, PasswordHash FROM Users WHERE LoginId = @LoginId"
Using command As New SqlClient.SqlCommand(sql, connection)
command.Parameters.Add("@LoginId", SqlDbType.NVarChar, 50).Value = loginId
Using reader = command.ExecuteReader()
If reader.Read() Then
' パスワード検証処理
End If
End Using
End Using
パラメータ化クエリを使うことで、入力値はSQL文ではなく値として扱われます。 SQLインジェクション対策として、すべての可変値はパラメータとして渡す必要があります。
SQLインジェクション対策の基本
- SQLを文字列連結で組み立てない
- 必ずパラメータ化クエリを使用する
- ストアドプロシージャ内の動的SQLにも注意する
- DBユーザーに必要以上の権限を与えない
- エラー詳細を画面にそのまま表示しない
- 入力値検証と組み合わせる
SQLインジェクション対策は、アプリケーション側だけでなくDB権限設計も含めて考える必要があります。 たとえば、検索用ユーザーにDELETE権限を与えないなど、被害を最小化する設計が重要です。
4. XSS・パストラバーサル対策
VB.NETのデスクトップアプリでは、WebアプリほどXSSが問題になる場面は多くありません。 しかし、HTMLを生成する機能、WebBrowserコントロール、帳票プレビュー、メール本文生成、 Web API連携などでは注意が必要です。
XSSとは
XSSは、Cross Site Scriptingの略で、画面に表示する文字列に悪意あるスクリプトを混入させる攻撃です。 Web画面やHTML出力を扱う場合は、表示前にHTMLエンコードを行う必要があります。
危険なHTML生成の例
Dim html As String = "<div>" & userName & "</div>"
userNameにHTMLやJavaScriptが含まれている場合、そのままHTMLとして解釈される可能性があります。
HTMLエンコードの考え方
Dim safeUserName As String = System.Net.WebUtility.HtmlEncode(userName)
Dim html As String = "<div>" & safeUserName & "</div>"
HTMLとして出力する値は、必ずエンコードしてから表示することが重要です。
パストラバーサルとは
パストラバーサルとは、ファイルパスに「../」や「..\」のような相対パスを混入させ、 本来アクセスできないファイルへアクセスする攻撃です。
危険なファイルパス処理
Dim filePath As String = "C:\files\" & inputFileName
Dim content As String = IO.File.ReadAllText(filePath)
inputFileNameに「..\..\secret.txt」のような値が入ると、意図しない場所のファイルを読まれる可能性があります。
パストラバーサル対策
- ユーザー入力をそのままファイルパスに使わない
- ファイル名のみを許可する
- 許可された拡張子のみ扱う
- 保存先ディレクトリを固定する
- GetFullPathで最終パスを確認する
- 指定ディレクトリ外へのアクセスを拒否する
安全なファイルパス確認例
Public Function GetSafeFilePath(baseDirectory As String, fileName As String) As String
Dim safeFileName As String = IO.Path.GetFileName(fileName)
Dim fullPath As String = IO.Path.GetFullPath(IO.Path.Combine(baseDirectory, safeFileName))
Dim basePath As String = IO.Path.GetFullPath(baseDirectory)
If Not fullPath.StartsWith(basePath) Then
Throw New UnauthorizedAccessException("不正なファイルパスです。")
End If
Return fullPath
End Function
ファイル操作では、ユーザー入力を信用せず、必ず安全なパスへ正規化してから処理する必要があります。
5. パスワードハッシュ化
パスワードは、平文で保存してはいけません。 万が一データベースが漏洩した場合、平文パスワードが保存されていると、 利用者のアカウントがそのまま危険にさらされます。
パスワードは、復号できる暗号化ではなく、ハッシュ化して保存するのが基本です。 ハッシュ化とは、元の値から固定長の値を生成する処理であり、原則として元のパスワードへ戻せません。
悪い例:平文保存
Dim sql As String = "INSERT INTO Users (LoginId, Password) VALUES (@LoginId, @Password)"
このようにPassword列へ平文のパスワードを保存する設計は避けるべきです。
ハッシュ化の基本方針
- パスワードは平文保存しない
- 復号可能な暗号化ではなくハッシュ化する
- ソルトを利用する
- 十分な計算コストを持つ方式を利用する
- ログにパスワードを出力しない
- パスワード再発行時も元のパスワードを通知しない
簡易的なハッシュ化例
Public Function ComputeSha256Hash(value As String) As String
Using sha256 As Security.Cryptography.SHA256 = Security.Cryptography.SHA256.Create()
Dim bytes As Byte() = Text.Encoding.UTF8.GetBytes(value)
Dim hashBytes As Byte() = sha256.ComputeHash(bytes)
Return BitConverter.ToString(hashBytes).Replace("-", "").ToLower()
End Using
End Function
この例はハッシュ化の仕組みを理解するための簡易例です。 実際のパスワード保存では、単純なSHA-256のみではなく、ソルトやストレッチングを含む専用のパスワードハッシュ方式を検討する必要があります。
パスワード検証の考え方
Dim inputPasswordHash As String = ComputeSha256Hash(inputPassword)
If inputPasswordHash = storedPasswordHash Then
' ログイン成功
Else
' ログイン失敗
End If
ログイン時には、入力されたパスワードを同じ方法でハッシュ化し、 DBに保存されているハッシュ値と比較します。 元のパスワードを復号して比較する設計にはしません。
6. 暗号化と復号
暗号化は、データを第三者に読めない形へ変換する処理です。 ハッシュ化と異なり、暗号化されたデータは鍵を使って復号できます。
業務システムでは、接続文字列、APIキー、個人情報、外部連携用の認証情報など、 復号が必要な機密情報に対して暗号化を検討します。
ハッシュ化と暗号化の違い
| 項目 | ハッシュ化 | 暗号化 |
|---|---|---|
| 元に戻せるか | 戻せない | 鍵があれば戻せる |
| 主な用途 | パスワード保存 | 機密情報の保護 |
| 比較方法 | 同じ入力をハッシュ化して比較 | 復号して利用 |
暗号化対象の例
- APIキー
- 外部連携パスワード
- 秘密鍵
- 個人情報
- 設定ファイル内の接続情報
暗号化設計の注意点
- 暗号鍵をソースコードに直接書かない
- 暗号鍵の保管場所を分離する
- 復号できる人や処理を限定する
- ログに復号後の値を出力しない
- バックアップファイルも保護対象にする
- 鍵のローテーションを検討する
暗号化は、実装方法だけでなく鍵管理が非常に重要です。 暗号鍵をソースコードや設定ファイルに平文で保存している場合、 暗号化していても実質的な保護にならない可能性があります。
7. Windows認証
社内向けのVB.NET業務システムでは、Windows認証を利用することがあります。 Windows認証では、利用者がWindowsにログインしているユーザー情報をもとに認証を行います。
これにより、アプリケーション側で独自にログインIDとパスワードを管理しなくても、 社内のWindowsアカウントやActive Directoryと連携した認証が可能になります。
現在のWindowsユーザーを取得する例
Dim currentUser As String = Environment.UserName
Dim domainUser As String = Security.Principal.WindowsIdentity.GetCurrent().Name
Console.WriteLine(currentUser)
Console.WriteLine(domainUser)
Environment.UserNameではユーザー名を取得できます。 WindowsIdentity.GetCurrent().Nameでは、ドメイン名を含むユーザー情報を取得できます。
Windows認証のメリット
- 独自パスワード管理が不要になる
- 社内アカウント管理と連携しやすい
- 退職者や異動者の権限管理を一元化しやすい
- 利用者にとってログイン操作が簡単になる
Windows認証の注意点
- 社外利用や別ドメイン環境では制約がある
- アプリケーション独自の権限管理は別途必要になる
- 共有PC利用時はユーザー特定に注意する
- 実行ユーザーとDB接続ユーザーの違いを理解する
Windows認証は便利ですが、認証できたからといって、すべての操作を許可してよいわけではありません。 認証と認可を分けて考える必要があります。
8. ロールベースアクセス制御
認証は「誰であるか」を確認する仕組みです。 一方、認可は「その人が何をしてよいか」を判断する仕組みです。
業務システムでは、ログインできるユーザーであっても、 すべての画面や操作を利用できるとは限りません。 たとえば、一般社員、管理者、経理担当、人事担当、承認者などで利用できる機能は異なります。
ロールの例
- 一般ユーザー
- 管理者
- 承認者
- 経理担当
- 人事担当
- 閲覧専用ユーザー
権限制御の例
| 機能 | 一般ユーザー | 承認者 | 管理者 |
|---|---|---|---|
| 申請登録 | 可 | 可 | 可 |
| 申請承認 | 不可 | 可 | 可 |
| ユーザー管理 | 不可 | 不可 | 可 |
| 監査ログ閲覧 | 不可 | 不可 | 可 |
Role Enumの例
Public Enum UserRole
General
Approver
Admin
End Enum
権限チェックの例
Public Class AuthorizationService
Public Function CanApprove(role As UserRole) As Boolean
Return role = UserRole.Approver OrElse role = UserRole.Admin
End Function
Public Function CanManageUsers(role As UserRole) As Boolean
Return role = UserRole.Admin
End Function
End Class
画面側での制御例
btnApprove.Enabled = _authorizationService.CanApprove(currentUser.Role)
btnUserManagement.Visible = _authorizationService.CanManageUsers(currentUser.Role)
権限がない操作は、ボタンを非表示または無効化します。 ただし、画面上でボタンを隠すだけでは不十分です。 Service側でも必ず権限チェックを行う必要があります。
Service側での権限チェック
Public Sub Approve(applicationId As Integer, currentUser As LoginUser)
If Not _authorizationService.CanApprove(currentUser.Role) Then
Throw New UnauthorizedAccessException("承認権限がありません。")
End If
' 承認処理
End Sub
画面制御はユーザー体験のため、Service側の権限チェックはセキュリティのために行います。 画面で非表示にしていても、内部処理が直接呼ばれる可能性を考慮する必要があります。
9. 操作ログ・監査ログ
操作ログや監査ログは、誰が、いつ、どのデータに対して、何を行ったかを記録するためのログです。 障害解析だけでなく、不正操作の追跡、内部統制、監査対応にも利用されます。
操作ログに記録する情報
- 操作日時
- ユーザーID
- ユーザー名
- 画面名
- 機能名
- 操作種別
- 対象データID
- 操作結果
- 端末名
- IPアドレス
操作ログテーブルの例
OperationLogs
- LogId
- OperationDateTime
- UserId
- UserName
- FunctionName
- OperationType
- TargetId
- Result
- ClientName
- Message
操作ログ出力の例
Public Class OperationLogService
Public Sub WriteLog(user As LoginUser, functionName As String, operationType As String, targetId As String, result As String)
' DBやファイルへ操作ログを出力する
' UserId, FunctionName, OperationType, TargetId, Result, DateTimeなどを保存する
End Sub
End Class
操作ログを残すべき操作
- ログイン
- ログアウト
- データ登録
- データ更新
- データ削除
- 承認・差戻し
- 権限変更
- CSV出力
- 帳票出力
- 個人情報の閲覧
特に、個人情報の閲覧やCSV出力は、情報持ち出しにつながる可能性があるため、 誰が出力したかを記録することが重要です。
監査ログ設計の注意点
- 利用者が簡単に改ざんできない場所に保存する
- 削除操作そのものも記録する
- ログの保存期間を決める
- ログに機密情報を出しすぎない
- 検索しやすい形式で保存する
- システムログと操作ログを分ける
10. 機密情報の管理
機密情報とは、漏洩すると業務や利用者に重大な影響を与える情報です。 VB.NET業務システムでは、設定ファイル、ログ、DB、出力ファイル、ソースコードなど、 さまざまな場所に機密情報が入り込む可能性があります。
機密情報の例
- DB接続文字列
- DBユーザーのパスワード
- APIキー
- アクセストークン
- SFTP認証情報
- 秘密鍵
- 個人情報
- 給与情報
- 契約情報
避けるべき実装
Dim connectionString As String =
"Data Source=server;Initial Catalog=AppDb;User ID=admin;Password=password123"
このように、接続情報やパスワードをソースコードに直接書くのは危険です。 ソースコード管理システムに認証情報が残り、漏洩リスクが高まります。
機密情報管理の基本方針
- ソースコードに秘密情報を書かない
- 設定ファイルのアクセス権限を制限する
- 必要に応じて設定値を暗号化する
- ログに機密情報を出力しない
- 出力ファイルの保存場所を制限する
- 不要になったファイルは安全に削除する
- 本番環境と開発環境の認証情報を分ける
ログ出力時のマスキング例
Public Function MaskEmail(email As String) As String
If String.IsNullOrWhiteSpace(email) OrElse Not email.Contains("@") Then
Return "***"
End If
Dim parts = email.Split("@"c)
Dim namePart = parts(0)
If namePart.Length <= 2 Then
Return "***@" & parts(1)
End If
Return namePart.Substring(0, 2) & "***@" & parts(1)
End Function
個人情報をログに出す必要がある場合でも、すべてをそのまま出力するのではなく、 必要最小限の情報だけをマスキングして出力します。
11. セキュアなファイル出力設計
CSV、Excel、PDFなどのファイル出力では、出力されたファイル自体が情報漏洩リスクになります。 特に個人情報や売上情報を含むファイルは、保存場所、アクセス権限、ファイル名、削除タイミングを設計する必要があります。
ファイル出力時のリスク
- 誰でも見られる共有フォルダへ出力される
- 一時ファイルが削除されず残る
- ファイル名に個人情報が含まれる
- 出力ログが残っていない
- 退職者や異動者が古いファイルへアクセスできる
安全なファイル出力のポイント
- 出力先フォルダの権限を制限する
- ファイル名に個人情報を含めない
- 一時ファイルは処理後に削除する
- 出力者、出力日時、対象条件を操作ログに残す
- 必要に応じてファイルを暗号化する
- 保存期間を決める
安全なファイル名生成例
Public Function GenerateExportFileName(prefix As String) As String
Dim timestamp As String = DateTime.Now.ToString("yyyyMMddHHmmss")
Dim randomPart As String = Guid.NewGuid().ToString("N").Substring(0, 8)
Return prefix & "_" & timestamp & "_" & randomPart & ".csv"
End Function
ファイル名には、顧客名や個人名などの機密情報を含めず、 日時やランダム値を組み合わせて一意になるようにします。
12. 実務でありがちなセキュリティ問題
画面でボタンを隠しているだけ
権限のないユーザーにボタンを非表示にするだけでは不十分です。 Service側でも必ず権限チェックを行う必要があります。
SQLを文字列連結で作っている
検索条件やログイン情報をSQLへ直接連結すると、SQLインジェクションの危険があります。 必ずパラメータ化クエリを使います。
パスワードを平文保存している
パスワードを平文でDBに保存していると、DB漏洩時に利用者の認証情報がそのまま漏洩します。 パスワードはハッシュ化して保存します。
ログに機密情報を出している
調査目的であっても、パスワード、APIキー、個人番号、カード番号などをログに出力してはいけません。 必要な情報はマスキングして出力します。
設定ファイルに認証情報を平文で置いている
接続文字列やAPIキーを設定ファイルに置く場合は、ファイル権限や暗号化を検討する必要があります。
操作ログが不足している
誰が重要データを更新・削除・出力したか分からないシステムは、監査や障害調査に弱くなります。 重要操作は必ず操作ログを残します。
13. 演習課題
演習1:入力値検証クラスを作成する
ユーザー登録DTOに対して、必須チェック、文字数チェック、メール形式チェックを行うValidatorクラスを作成してください。
演習2:SQLインジェクション対策を実装する
ログインIDを条件にユーザーを検索する処理を、文字列連結ではなくパラメータ化クエリで実装してください。
演習3:権限制御を実装する
一般ユーザー、承認者、管理者の3つのロールを定義し、 承認ボタンとユーザー管理ボタンの表示・有効制御を実装してください。
演習4:Service側の認可チェックを実装する
画面側でボタンを無効化するだけでなく、 Service側でも承認権限があるか確認し、権限がない場合は例外を発生させてください。
演習5:操作ログを設計する
ログイン、データ登録、データ削除、CSV出力の操作ログに、 どのような項目を記録すべきか設計してください。
演習6:ログ出力時のマスキングを実装する
メールアドレスや電話番号などの個人情報をログ出力する場合に、 一部をマスキングする関数を作成してください。
演習7:安全なファイル名を生成する
CSV出力時に、個人名や顧客名を含めず、 日時とランダム値を組み合わせた一意なファイル名を生成してください。
14. まとめ
本章では、VB.NET業務システムにおけるセキュリティ設計、認証、認可について学習しました。
セキュリティ設計では、ログイン機能だけでなく、入力値検証、SQLインジェクション対策、 XSS対策、パストラバーサル対策、パスワードハッシュ化、暗号化、権限制御、操作ログ、 機密情報管理などを総合的に考える必要があります。
入力値は、画面入力だけでなく、CSV、Excel、外部API、ファイル名なども含めて信用せず、 必ず検証することが重要です。 SQLを実行する場合は、文字列連結を避け、必ずパラメータ化クエリを使用します。
認証は「誰であるか」を確認する仕組みであり、認可は「何をしてよいか」を判断する仕組みです。 画面上でボタンを非表示にするだけでは不十分であり、Service側でも権限チェックを行う必要があります。
パスワードは平文で保存せず、ハッシュ化して保存します。 復号が必要な機密情報については暗号化を検討しますが、暗号鍵の管理を誤ると十分な保護になりません。
操作ログや監査ログは、障害調査、不正操作の追跡、内部統制、監査対応において重要です。 誰が、いつ、どの機能で、どのデータに対して、何を行ったかを記録できる設計にします。
また、ログや出力ファイルに機密情報を含めすぎないことも重要です。 必要な情報だけを記録し、個人情報や認証情報はマスキング、暗号化、アクセス制御などで保護します。
本章のゴールは、単にセキュリティ機能を追加することではなく、 業務システムとして安全性、運用性、監査性を備えた設計を行えるようになることです。 この考え方は、長期運用されるVB.NET業務システムの信頼性を支える重要な基礎となります。