Azure SQL Databaseの監査と脅威検出をHIPAA対応で構成する完全ガイド|Log Analytics・Storage(WORM)・Microsoft Sentinel連携

電子カルテ(EHR)で Premium vCore モデルの Azure SQL Database を用い、PHI(保護対象医療情報)へのアクセスを厳格に監査しつつ、機械学習ベースの脅威検出でインシデントを即応する――この要件を満たすための設計と実装を、運用に乗るレベルまで具体的に解説します。監査の改ざん防止、長期保管、Microsoft Sentinel 連携、HIPAA/HITECH のセクション 164.312 に対応する実装ポイントを“そのまま使える手順”でまとめました。

目次

全体像:監査・脅威検出・対応を一体化するアーキテクチャ

本記事のゴールは、Azure SQL Database 上で発生する「誰が・いつ・何に・どうアクセスしたか」を改ざん不可能な形で残し、異常を早期に検知し、Microsoft Sentinel(SIEM/SOAR)での自動対応までを一貫運用できる状態を作ることです。アプリケーションは Microsoft Entra ID(旧 Azure AD)による認証・認可を前提とし、ワークロード・ネットワーク・鍵管理をゼロトラストで統合します。

リファレンス構成(テキスト図)

[EHR アプリ(Managed Identity / OIDC)]
        │    └─ Private Link / Azure Firewall / NSG
        ▼
  [Azure SQL Database(Premium vCore)]
        ├─ SQL 監査 → Log Analytics(検索/ダッシュボード)
        │             └─ Data Export / アーカイブ → Azure Storage(WORM)
        ├─ Microsoft Defender for SQL(脅威検出アラート)
        │             └─ Log Analytics(SecurityAlert)
        └─ Azure Policy(構成の強制/ドリフト検出)

[Microsoft Sentinel(SIEM/SOAR)]
├─ 解析ルール(KQL)
├─ インシデント自動化(Logic Apps/Playbooks)
└─ 通知(Teams/PagerDuty/Email) 

主なコンポーネントと役割

要素役割HIPAA の観点
Azure SQL Auditingログイン/DML/DDL を証跡化し、Log Analytics/Storage に出力Audit Controls(164.312(b))
Microsoft Defender for SQLSQL インジェクション、異常ログイン、データ持ち出し等の検出Security Incident Procedures(164.308(a)(6))
Azure Storage(WORM)監査証跡の改ざん防止&長期保存(時間ロック/法的ホールド)Integrity & Non‑repudiation
Log AnalyticsKQL 検索、可視化、Sentinel へのデータ供給継続的モニタリング
Microsoft Sentinel相関検知と自動対応(SOAR)インシデント対応の自動化と証跡化
TDE(CMK/BYOK)+ Key Vaultデータ暗号化と鍵の分離管理、年1回以上のローテーションEncryption & Key Management(164.312(a)(2)(iv))
Entra ID(AD-only 認証)SQL 認証の排除、MFA/CA/PIM による統制Access Control(164.312(a)(1)、(d))

前提条件とベースライン(ゼロトラスト設定)

  • Entra ID のみを許可:論理サーバーで「Active Directory のみの認証」を有効化し、SQL ログインは無効化。
  • 特権アクセスの一時化:Privileged Identity Management(PIM)で「SQL Server Administrator(Entra 組織管理者)」を JIT 付与、MFA と承認フロー必須。
  • ネットワークの秘匿化:Private Endpoint を使用し、サーバーの「パブリック ネットワーク アクセス無効化」。必要時は Azure Firewall/Firewall Policy で egress を最小化。
  • アプリの機密性:接続は Managed Identity(推奨)または OIDC クライアント資格情報。機密情報は Key Vault 参照。
  • TLS 強制:DB 接続は TLS 1.2 以上、Encrypt=True, TrustServerCertificate=False を徹底。

監査のスコープ設計と出力先(改ざん不可・検索性の両立)

推奨スコープ

サーバー(論理サーバー)単位で監査を有効化し、全 DB をデフォルトでカバー。PHI が乗る一部 DB/テーブルのみ、追加の詳細監査をデータベース側で上書きする二層構成が現実的です。

出力先の選び分け

出力先用途構成ポイント
Log Analytics調査・相関分析(KQL)/ Sentinel 連携ワークスペース当たりの保持期間を 12–24 か月。必要に応じてテーブル単位の保持とアーカイブを併用。
Azure Storage(Blob)WORM で 6 年以上の長期保管コンテナーに不変ポリシー(時間ロック/法的ホールド)を設定。SSE with CMK、アクセスは RBAC 最小権限。
併用検索性と改ざん耐性の両取り監査設定で Log Analytics と Storage を同時送信。運用の第一タッチは Log Analytics、監査委員会向けは WORM 出力。

監査イベント(Action Groups)の推奨セット

Action Group目的注意点
SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP成功ログインの追跡(誰が入ったか)アプリケーション ID と人の ID を分けて可視化。
FAILED_DATABASE_AUTHENTICATION_GROUPブルートフォース/不正試行の検出Sentinel でしきい値超過をアラート化。
SCHEMA_OBJECT_CHANGE_GROUPDDL(スキーマ/権限)変更の証跡化変更申請と突合用の根拠に。
DATABASE_OBJECT_ACCESS_GROUPDML(SELECT/UPDATE/DELETE)アクセスの証跡SELECT のパラメータ値は記録されない点に注意(後述)。

ポータルでの有効化(サーバー単位)

  1. 「SQL サーバー」→「監査」→ オン。
  2. 出力先で「Log Analytics を有効化」「ストレージを有効化」を両方オン。
  3. Log Analytics ワークスペースを選択し、保持期間(例:18 か月)を設定。
  4. ストレージは専用アカウントに分離し、コンテナーに不変ポリシー(例:2190 日 = 6 年)を設定。

CLI/自動化例

Entra ID のみの認証・パブリック無効化

az sql server ad-only-auth update -g <rg> -n <server> --enable true
az sql server update -g <rg> -n <server> --public-network-access Disabled

監査(サーバー):Log Analytics + Storage 併用

az sql server audit-policy update -g <rg> -n <server> \
  --state Enabled \
  --is-azure-monitor-target-enabled true \
  --log-analytics-workspace-resource-id </subscriptions/.../resourceGroups/.../providers/Microsoft.OperationalInsights/workspaces/<LAW>> \
  --storage-account <storageAccountName> --retention-days 0 \
  --audit-actions-and-groups "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP" \
                             "FAILED_DATABASE_AUTHENTICATION_GROUP" \
                             "SCHEMA_OBJECT_CHANGE_GROUP" \
                             "DATABASE_OBJECT_ACCESS_GROUP"

Blob 不変ポリシー(時間ロック)

az storage container create -n sqlaudit --account-name <sa>
# 2190 日(約 6 年)の WORM(追記のみ許可)
az storage container immutability-policy create \
  --account-name <sa> --container-name sqlaudit --period 2190 \
  --allow-protected-append-write true
# 返却された ETag を使ってロック確定(以後は短縮不可)
# az storage container immutability-policy lock ...

Bicep(監査・Defender・VA)

resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' existing = {
  name: 'my-sqlsrv'
}

resource audit 'Microsoft.Sql/servers/auditingSettings@2023-05-01-preview' = {
name: 'default'
parent: sqlServer
properties: {
state: 'Enabled'
isAzureMonitorTargetEnabled: true
logAnalyticsWorkspaceResourceId: lawId
storageEndpoint: 'https://.blob.core.windows.net/'
storageAccountSubscriptionId: subscription().subscriptionId
retentionDays: 0
auditActionsAndGroups: [
'SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP'
'FAILED_DATABASE_AUTHENTICATION_GROUP'
'SCHEMA_OBJECT_CHANGE_GROUP'
'DATABASE_OBJECT_ACCESS_GROUP'
]
}
}

resource sap 'Microsoft.Sql/servers/securityAlertPolicies@2023-05-01-preview' = {
name: 'default'
parent: sqlServer
properties: {
state: 'Enabled'
emailAccountAdmins: 'Enabled'
emailAddresses: '[[email protected]](mailto:[email protected])'
disabledAlerts: ''
}
}

resource va 'Microsoft.Sql/servers/vulnerabilityAssessments@2021-02-01-preview' = {
name: 'default'
parent: sqlServer
properties: {
storageContainerPath: 'https://.blob.core.windows.net/va'
recurringScans: {
isEnabled: true
emailSubscriptionAdmins: true
emails: [ '[[email protected]](mailto:[email protected])' ]
}
}
} 

Microsoft Defender for SQL(旧 ATP):脅威検出の設計

Defender for SQL は、 Azure SQL Database に対する機械学習ベースの検出を提供します。典型例は以下です。

  • SQL インジェクション:アプリ層を経由した異常パターンを識別。
  • 異常ログイン:未知の IP、地理的に不自然な散逸、クレデンシャル詐取兆候。
  • データ持ち出し:短時間の大量 SELECT/BCP などの異常振る舞い。

発報は Log Analytics の SecurityAlert テーブルに取り込まれ、Sentinel の分析ルールでインシデント化できます。Playbook(Logic Apps)で「疑わしい IP を SQL サーバーのファイアウォールから即時遮断」「該当ユーザーの PIM ロールを手動承認に切り替え」「PagerDuty/Teams へ即通知」といった自動化が可能です。

Log Analytics(KQL)での実践的な可視化・検知

監査ログは SQLSecurityAuditEvents テーブル、Defender のアラートは SecurityAlert テーブルに格納される想定で、運用にそのまま使える KQL の雛形を示します(列名は環境で差異があるため project/extend で調整してください)。

過去 7 日間の監査イベント内訳

SQLSecurityAuditEvents
| where TimeGenerated > ago(7d)
| summarize count() by tostring(action_id_s)
| order by count_ desc

失敗ログインの多発ユーザー/IP

SQLSecurityAuditEvents
| where TimeGenerated > ago(24h)
| where tostring(action_id_s) has 'FAILED_DATABASE_AUTHENTICATION'
| summarize Failed=count() by User=tostring(database_principal_name_s), IP=tostring(client_ip_s)
| where Failed >= 5
| order by Failed desc

PHI テーブル(例:dbo.Patient)への DML 監査

let PhiTables = dynamic(['dbo.Patient','dbo.Exam','dbo.Claim']);
SQLSecurityAuditEvents
| where TimeGenerated > ago(7d)
| where tostring(action_id_s) has 'DATABASE_OBJECT_ACCESS'
| where tostring(object_name_s) in (PhiTables)
| project TimeGenerated, User=tostring(server_principal_name_s),
         Object=tostring(object_name_s), Statement=substring(tostring(statement_s), 0, 200),
         Succeeded=coalesce(succeeded_b, true), ClientIP=tostring(client_ip_s)
| order by TimeGenerated desc

業務時間外アクセス(平日 9–18 時以外)の監視

let BusinessHours = 9..18;
SQLSecurityAuditEvents
| where TimeGenerated > ago(7d)
| where datetime_part('hour', TimeGenerated) !in (BusinessHours)
| where tostring(action_id_s) has 'DATABASE_OBJECT_ACCESS'
| summarize Access=count() by User=tostring(server_principal_name_s)
| where Access > 100
| order by Access desc

Defender アラートの高優先度一覧

SecurityAlert
| where TimeGenerated > ago(30d)
| where AlertSeverity in ('High','Medium')
| where ProductName has 'SQL' or VendorOriginalProductName has 'SQL'
| project TimeGenerated, AlertName, AlertSeverity, CompromisedEntity, Description
| order by TimeGenerated desc

Sentinel でのインシデント化と自動対応

コネクタとデータスキーマ

  • データコネクタ:Azure SQL Database Auditing(Log Analytics 経由)、Microsoft Defender for Cloud(SecurityAlert)。
  • インシデントルール:分析ルール(KQL)で条件を定義。ベースラインしきい値は最初の 2–4 週の観測から決定。

大量データ持ち出しの疑い(疑似例)

let threshold = 10000; // セッション当たりの SELECT 回数のしきい値例
SQLSecurityAuditEvents
| where TimeGenerated > ago(1d)
| where tostring(action_id_s) has 'DATABASE_OBJECT_ACCESS'
| where tostring(statement_s) startswith_cs 'SELECT'
| summarize Selects=count() by bin(TimeGenerated, 1h), User=tostring(server_principal_name_s), IP=tostring(client_ip_s)
| where Selects > threshold

上記をベースに Analytics ルールを作成し、Playbook で「IP 遮断」「一時的に DB へのアプリ マネージド ID を無効化」「チケット発行」を自動化します。

よく使う自動化(Playbooks)の例

トリガ自動アクション備考
High の Defender for SQL アラート疑わしい送信元 IP をファイアウォールから遮断SQL サーバーの公開を無効化していれば、Private Link 側の NSG/Azure Firewall で遮断
短時間の失敗ログイン多発該当アカウントの PIM ロールを停止、MFA 強制、Teams 通知Entra ID の条件付きアクセスと連携
PHI テーブルへの業務時間外大量 SELECTSOC へのメンション通知、該当接続文字列の回転Key Vault のバージョンを切り替え、アプリはマネージド ID で自動反映

暗号化と鍵管理(TDE/CMK、Always Encrypted)

PHI を扱うため、保存時暗号化(at rest)は TDE を前提とし、顧客管理キー(CMK/BYOK)でサーバーの TDE プロテクターを置き換えます。鍵は Key Vault または Managed HSM で管理し、年 1 回以上のローテーション、アクセスは Managed Identity 経由に限定します。

TDE プロテクターの CMK への切替(CLI)

# Key Vault で RSA キーを用意(省略)
# SQL サーバーの TDE プロテクターを CMK に変更
az sql server tde-key set -g <rg> -s <server> \
  --kid https://<kv>.vault.azure.net/keys/<keyName>/<version> \
  --type AzureKeyVault

監査ログの保管先(Storage)も SSE with CMK とし、鍵はアプリ運用者と分離した「監査・鍵管理チーム」のみがアクセスできるように RBAC を分離します。

Always Encrypted(必要に応じて)

患者のマイナンバー等の特定個人識別符号を含む列は Always Encrypted(Secure Enclaves) を併用することで、DB サーバー側で復号せず計算を許可できます。アプリケーション ドライバー設定とキーの運用が必要になります。

PHI 単位の追跡をどう実現するか(SELECT パラメータが残らない課題への対処)

標準の SQL 監査では SELECT のパラメータ値(例:患者 ID)が記録されません。どの患者が閲覧されたかの粒度が必要な場合は、次のいずれかを組み合わせます。

  • アプリ層での明示的なトレーシング:Application Insights 等に「患者 ID」「リクエスト ID」「ユーザー ID」を記録し、SQL 監査の session_id または接続の相関 ID と突合。
  • Row-Level Security(RLS)+ SESSION_CONTEXT:アプリが SESSION_CONTEXT('actor') へ患者またはテナント情報を投入し、RLS ポリシーで許可/拒否を制御。監査上は「誰がどのテーブルにアクセスしたか」を残しつつ、アプリ側ログで患者 ID を復元できるようにします。

RLS と SESSION_CONTEXT の雛形

-- セッションにアクター情報を設定(アプリ接続直後)
EXEC sys.sp_set_session_context @key = N'actor', @value = @UserObjectId;

-- RLS: 患者テーブルにアクセス制御(例)
CREATE SCHEMA sec;
GO
CREATE FUNCTION sec.fn_rls_patient(@owner UNIQUEIDENTIFIER)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS accessGranted
WHERE @owner = CAST(SESSION_CONTEXT(N'actor') AS UNIQUEIDENTIFIER);
GO
CREATE SECURITY POLICY sec.policy_patient
ADD FILTER PREDICATE sec.fn_rls_patient(owner_id) ON dbo.Patient
WITH (STATE = ON); 

この方式により、SQL 監査とアプリ監査を合わせて「誰が・どの患者レコードに・いつアクセスしたか」を再構成できます。

運用:ダッシュボード、定期評価、テスト

  • ワークブック:Log Analytics で「失敗ログインの推移」「PHI テーブル別 DML」「DDL 変更の時系列」を可視化。Sentinel の Workbook として SOC に共有。
  • 脆弱性評価(VA):月次のレポートを DBA/SOC が確認し、High は 2 週間以内に是正。
  • 四半期テスト:擬似 SQLi、疑似 BCP ダンプ、業務時間外の連続 SELECT をシナリオ化して発報〜自動化の有効性を検証。
  • 鍵ローテーション訓練:年 1 回、TDE プロテクターのローテーション演習と復旧手順のレビュー。

運用ダッシュボードの KPI 例

KPI指標目安
失敗ログイン率失敗/総ログイン< 2%
高深刻度アラート TTD検出までの時間< 5 分
高深刻度アラート TTR封じ込めまでの時間< 15 分(自動化で短縮)
未解決インシデントオープン件数= 0 を維持

Azure Policy で“設定抜け”を防ぐ

監査や Defender を「必須化」するには Azure Policy が有効です。代表例:

  • SQL サーバーは監査を有効にし Log Analytics へ送る(DeployIfNotExists)
  • SQL サーバーは AD-only 認証を強制
  • SQL データベースは TDE を有効(CMK)
  • ストレージは不変ポリシーが設定済み

イニシアチブとして環境に割り当て、準拠状況をセキュリティ委員会に定期レポートします。

HIPAA/HITECH の技術的保護要件へのマッピング

要件対応実装補足
Access Control(164.312(a))Entra ID のみ、PIM、RLS、最小権限、Private Link管理者アクセスは JIT、MFA 必須
Audit Controls(164.312(b))SQL 監査(ログイン/DML/DDL)、WORM 保管保持 6 年以上、改ざん不可
Integrity(164.312(c))TDE(CMK)、ストレージの不変化、ハッシュ検証(必要時)鍵とデータの分離管理
Person/Entity Authentication(164.312(d))Entra ID + MFA、SQL ログイン無効化サービス プリンシパル/Managed Identity を分離
Transmission Security(164.312(e))TLS1.2+、Encrypt=True、Private Link証明書検証の強制

落とし穴とアンチパターン

  • DB 単位だけで監査を有効化:新設 DB の取りこぼしが発生。サーバー単位を既定に。
  • WORM を未設定:法的に有効な不可変性が担保できない。必ず時間ロック/法的ホールドを設定。
  • SQL ログインが残存:Entra ID(AD-only)に統一して除去。
  • ログ保持が短すぎる:Log Analytics は検索用、長期は Storage へ二重化。
  • アプリの患者 ID 追跡を DB 監査だけに依存:アプリ層の相関ログを必ず併用。

即導入できるチェックリスト

  • 論理サーバーで監査オン、Log Analytics と Storage(WORM)に同時出力。
  • 監査イベントは SUCCESSFUL/FAILED_AUTH、SCHEMA_OBJECT_CHANGE、DATABASE_OBJECT_ACCESS を基本セット。
  • Defender for SQL をオン、Soc 通知先・Sentinel 連携を設定。
  • Sentinel の分析ルールと Playbooks を有効化(IP 遮断、PIM 停止、通知)。
  • TDE(CMK/BYOK) へ切替、鍵は Key Vault/Managed HSM、年 1 回以上ローテーション。
  • AD-only 認証、MFA、PIM、Private Link に統一。
  • RLS + SESSION_CONTEXT、アプリ層ログで患者 ID を追跡可能に。
  • Azure Policy で必須設定を自動適用・監査。
  • VA 月次、監査/自動化の四半期テストを継続。

実装スニペット集(Terraform 例)

resource "azurerm_mssql_server_extended_auditing_policy" "srv" {
  server_id                          = azurerm_mssql_server.this.id
  log_monitoring_enabled             = true
  log_analytics_workspace_id         = azurerm_log_analytics_workspace.law.id
  storage_endpoint                   = azurerm_storage_account.sa.primary_blob_endpoint
  retention_in_days                  = 0
  audit_actions_and_groups           = [
    "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP",
    "FAILED_DATABASE_AUTHENTICATION_GROUP",
    "SCHEMA_OBJECT_CHANGE_GROUP",
    "DATABASE_OBJECT_ACCESS_GROUP"
  ]
}

resource "azurerm_mssql_server_security_alert_policy" "defender" {
resource_group_name = azurerm_resource_group.rg.name
server_name         = azurerm_mssql_server.this.name
state               = "Enabled"
email_account_admins = true
email_addresses     = ["[[email protected]](mailto:[email protected])"]
} 

ケーススタディに基づく運用のコツ

  • Premium vCore の利点を活かす:監査・Defender のオーバーヘッドは小さいが、ピーク時に影響が出ないよう IOPS/ログ出力帯域 を余裕持って確保。
  • テナント分離:医療機関/部門ごとに接続主体(Group Managed Identity)を分け、監査時のフィルタリングとブロックを精緻化。
  • ダッシュボードは“問い”から:「誰が、勤務時間外に、どの PHI テーブルをどれだけ閲覧したか」を 1 画面で把握できる設計にする。

まとめ

監査は「サーバー全体→必要に応じて DB 上書き」の二層で漏れを防ぎ、Defender for SQL の機械学習検出で未知の脅威を捕捉します。Log Analytics での可視化と Sentinel の自動化を組み合わせ、WORM による改ざん防止・長期保管を徹底すれば、HIPAA/HITECH の技術的保護要件に適合した監視・対応基盤が完成します。SELECT のパラメータ値を補うためのアプリ層ログや RLS を取り入れ、鍵管理(CMK)とネットワーク秘匿化(Private Link)を含む多層防御を実装すれば、EHR の現場でも実運用に耐えるセキュリティ態勢を維持できます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次