Salesforce 開発者向けガイド:Apexによるトランザクションセキュリティポリシーの実装

はじめに

こんにちは、Salesforce 開発者の視点から、Salesforce プラットフォームのセキュリティ機能を最大限に活用する方法について解説します。本日は、特に強力なリアルタイム監視・制御ツールである Transaction Security Policies (トランザクションセキュリティポリシー) に焦点を当て、その中でも Apex を用いた高度な実装方法について深掘りしていきます。

Salesforce 管理者であれば、宣言的なツール(Condition Builder)で基本的なポリシーを作成できるかもしれません。しかし、複雑なビジネスロジック、外部データとの連携、またはイベントオブジェクトのフィールドだけでは判断できない条件を実装するには、我々開発者の出番です。Apex を使うことで、トランザクションセキュリティポリシーの可能性を最大限に引き出し、組織のセキュリティをより堅牢で、かつ柔軟なものにすることが可能になります。


背景と応用シナリオ

Transaction Security Policies は、Real-Time Event Monitoring (リアルタイムイベントモニタリング) の一部であり、ユーザーのアクティビティをリアルタイムで監視し、事前に定義されたルールに違反した場合に特定のアクションを実行するフレームワークです。これにより、データ漏洩、不正アクセス、コンプライアンス違反などのセキュリティリスクを未然に防ぐことができます。

開発者としてこの機能を活用できる具体的なシナリオをいくつか見てみましょう。

シナリオ1:高度なデータエクスポート防止

課題: 「レポートから一度に1,000行を超えるデータをエクスポートするのを禁止する」という基本的な要件は Condition Builder で実現可能です。しかし、「特定のプロファイルに属するユーザーが、営業時間外に『機密』とタグ付けされたレポートから100行以上エクスポートしようとした場合にのみブロックする」といった複雑な条件はどうでしょうか。

解決策: Apex を使用すれば、レポートオブジェクト(Report)、ユーザーオブジェクト(User)、プロファイル(Profile)を照会し、現在時刻と比較するなど、複数のオブジェクトとロジックを組み合わせた高度な条件分岐を実装できます。

シナリオ2:API クエリのガバナンス

課題: 連携システムが大量のデータを一度に取得しようとし、組織のパフォーマンスに影響を与える可能性があります。特に、WHERE 句のない SOQL クエリが実行されると、大量のレコードが返され、API 制限に達するリスクがあります。

解決策: ApiEvent を監視するポリシーを作成します。Apex 内でイベントの QueriedEntities フィールドを解析し、特定のオブジェクト(例えば Account や Contact)に対して 2,000 レコードを超えるクエリが実行された場合にトランザクションをブロックすることができます。これにより、API の過剰な使用を防ぎ、システムの安定性を保ちます。

シナリオ3:リードコンバージョン時のカスタム検証

課題: リードを取引開始(コンバージョン)する際に、特定のカスタム項目が入力されていない、または特定の条件を満たしていない場合にプロセスを停止させたい。しかし、通常の入力規則では、リードオブジェクトから取引先や商談オブジェクトへのマッピングを跨いだ複雑な検証が難しい場合があります。

解決策: LeadConvertEvent をトリガーとするポリシーを Apex で実装します。Apex 内でリードの情報を評価し、コンバージョン先のオブジェクトに求められるデータが不足していると判断した場合、コンバージョン処理をブロックし、ユーザーにエラーメッセージを提示することが可能です。


原理説明

Transaction Security Policies の動作は、イベント駆動型アーキテクチャに基づいています。その仕組みを構成する主要な要素は以下の通りです。

  1. Events (イベント): ユーザーの操作や API コールによって発生する出来事です。例えば、レポートの実行 (ReportEvent)、API クエリ (ApiEvent)、ログイン試行 (LoginEvent) など、Salesforce が定義した様々なイベントが存在します。
  2. Policies (ポリシー): 特定のイベントを監視し、条件が満たされた場合に実行するアクションを定義したルールセットです。ポリシーは「有効」または「無効」の状態を持ち、有効なポリシーのみが評価されます。
  3. Conditions (条件): イベントが発生した際に、アクションをトリガーするかどうかを判断するロジックです。これは2つの方法で実装できます。
    • Condition Builder: GUI を使用して、クリック操作で条件を組み立てる方法。シンプルで直感的なルールに適しています。
    • Apex: TxnSecurity.PolicyCondition インターフェースを実装した Apex クラスを作成する方法。複雑で動的な条件判定が必要な場合に用います。本記事の主役です。
  4. Actions (アクション): 条件が満たされたときに実行される処理です。
    • Block: ユーザーの操作を完全にブロックします。
    • MultiFactorAuthentication: ユーザーに追加の本人確認(多要素認証)を要求します。
    • NoAction: 操作はブロックせず、通知のみを送信します(通常、関連する脅威検知フィードバックを作成)。

開発者として Apex を使用する場合、以下の流れで処理が実行されます。

ユーザーアクション → Salesforce イベント生成 → トランザクションセキュリティフレームワーク → Apex クラスの evaluate メソッド呼び出し → 判定結果 (true/false) を返す → 結果に基づきアクション (Block/MFA) を実行

この中で最も重要なのが、TxnSecurity.PolicyCondition インターフェースを実装した Apex クラスです。このクラスには evaluate(SObject event) というメソッドがあり、ここにポリシーの核心となるロジックを記述します。


示例代码

ここでは、Salesforce の公式ドキュメントでよく紹介される「大量のレポートエクスポートを防止する」ポリシーを例に、具体的な Apex コードを見ていきましょう。このコードは、2,000 行を超えるデータをエクスポート、または 5 つ以上のレポートを一度に実行しようとした場合に操作をブロックします。

まず、Apex クラスを作成します。

global class LargeReportExportPolicyCondition implements TxnSecurity.PolicyCondition {

    // evaluate メソッドは、トランザクションセキュリティポリシーがトリガーされるたびに呼び出されます。
    // 引数としてイベントに関する情報を持つ SObject を受け取ります。
    public boolean evaluate(SObject event) {
        
        // イベントのタイプに応じて処理を分岐します。
        // この例では ReportEvent を対象としています。
        switch on event.getSObjectType() {
            when ReportEvent.SObjectType {
                // SObject を具体的な ReportEvent 型にキャストします。
                // これにより、ReportEvent 固有のフィールドにアクセスできるようになります。
                ReportEvent reportEvent = (ReportEvent) event;

                // User-Agent をチェックして、非同期または UI 以外からのレポート実行を除外します。
                // 例えば、スケジューリングされたレポート実行などを対象外にするためです。
                // 'ReportAction' は、ユーザーが UI から直接レポートを実行またはエクスポートした場合に含まれることが多いです。
                if (System.Label.Transaction_Security_Report_Action.equals(reportEvent.UserAgent)) {
                    // 実行された操作(Operation)が 'Export' または 'Run' かどうかを確認します。
                    if ('Export'.equals(reportEvent.Operation) || 'Run'.equals(reportEvent.Operation)) {
                        
                        // プロファイル情報を取得して、特定のプロファイル(例:システム管理者)をポリシーの対象外にします。
                        // これにより、管理者は制限なくレポートをエクスポートできます。
                        Profile p = [SELECT Name FROM Profile WHERE Id = :UserInfo.getProfileId()];
                        if (p.Name == 'System Administrator') {
                            // システム管理者の場合は、常に false を返し、ポリシーをトリガーしません。
                            return false;
                        }

                        // エクスポートされた行数(RowsProcessed)が 2000 を超えるか、
                        // または実行されたレポートの数(NumberOfRecords)が 5 を超えるかを確認します。
                        if (reportEvent.RowsProcessed > 2000 || reportEvent.NumberOfRecords > 5) {
                            // 条件に一致した場合、true を返します。これによりポリシーのアクション(例:Block)が実行されます。
                            return true;
                        }
                    }
                }
            }
            // 他のイベントタイプを処理する場合は、ここに追加の when 節を記述します。
            when null {
                // イベントが null の場合は何もしません。
            }
            when else {
                // 予期しないイベントタイプの場合は何もしません。
            }
        }
        
        // 上記のどの条件にも一致しなかった場合は、false を返して操作を許可します。
        return false;
    }
}

この Apex クラスを作成した後、[設定] > [トランザクションセキュリティポリシー] に移動し、新しいポリシーを作成します。イベントとして「レポートイベント」を選択し、アクション条件として「Apex」を選択して、今作成した LargeReportExportPolicyCondition クラスを指定します。アクションを「ブロック」に設定すれば、ポリシーは完成です。


注意事項

Transaction Security Policies を Apex で実装する際には、いくつかの重要な点に注意する必要があります。

ライセンスと権限

この機能は、Salesforce Shield アドオン、または Real-Time Event Monitoring アドオンの一部として提供されます。利用するには適切なライセンス契約が必要です。また、ポリシーを作成・管理するには、「アプリケーションのカスタマイズ」および「すべてのデータの参照」権限を持つユーザーである必要があります。

パフォーマンスへの影響

ポリシーの Apex コードは、ユーザーのアクションと同期的に実行されます。つまり、コードの実行が完了するまでユーザーは待たされることになります。非効率なコード(例えば、ループ内での SOQL クエリや複雑な計算)は、ユーザーエクスペリエンスを著しく低下させる可能性があります。

evaluate メソッド内での SOQL クエリや DML ステートメントは極力避けるべきです。これらはパフォーマンスのボトルネックになりやすく、ガバナ制限に達するリスクもあります。上記のコード例のように、どうしても必要な場合は、極めて効率的に(インデックス付きの項目でフィルタリングするなど)実行する必要があります。

エラーハンドリング

Apex コード内でハンドルされない例外が発生した場合、トランザクションセキュリティのフレームワークは「フェイルクローズ」の原則に従います。つまり、セキュリティを優先し、デフォルトでユーザーの操作をブロックします。これは意図しない操作ブロックを引き起こす可能性があるため、try-catch ブロックを使用して堅牢なエラーハンドリングを実装することが強く推奨されます。

テストの難しさ

Apex クラス自体のロジックは、通常の Apex 単体テストで検証できます。しかし、ポリシーが実際に期待通りに動作するかどうかのエンドツーエンドのテストは、手動で行う必要があります。開発者 Sandbox でポリシーを有効にし、テストユーザーとしてログインして、実際にレポートをエクスポートするなどの操作を試す必要があります。

イベントオブジェクトの仕様

各イベントオブジェクト(ReportEvent, ApiEvent など)が持つフィールドは異なります。実装前に、開発者ガイドの「Real-Time Event Monitoring Objects」セクションで、利用したいイベントオブジェクトのフィールド定義を必ず確認してください。


まとめとベストプラクティス

Apex を利用した Transaction Security Policies は、Salesforce 組織のセキュリティをきめ細かく、かつ動的に制御するための非常に強力なツールです。宣言的な方法では実現不可能な、複雑なビジネス要件に対応できることが最大の利点です。

最後に、開発者としてこの機能を実装する際のベストプラクティスをまとめます。

  1. シンプル・イズ・ベスト: まずは Condition Builder で要件を満たせないか検討しましょう。Apex は、それが本当に必要な場合にのみ使用する「最終手段」と考えるのが賢明です。
  2. コードは軽量に: evaluate メソッド内のロジックは、可能な限りシンプルで高速に実行されるように設計してください。データベースアクセスや複雑な処理は避けましょう。
  3. カスタム表示ラベルの活用: コード例のように、閾値(例: 2000行)やプロファイル名などの設定値をハードコーディングするのではなく、カスタム表示ラベルやカスタムメタデータを使用することで、コードを変更せずに設定を管理できるようになり、保守性が向上します。
  4. 徹底的なテスト: Sandbox 環境で、様々なユーザープロファイルやシナリオを想定して、ポリシーの動作を徹底的にテストしてください。特に、意図しないブロックが発生しないかを入念に確認します。
  5. 明確なドキュメント: なぜそのポリシーが必要なのか、どのようなロジックで動作するのかを、コードのコメントや設定画面の説明欄に明確に記述し、将来の自分や他の管理者が理解できるようにしておきましょう。

Transaction Security Policies を使いこなすことで、我々開発者は、単なる機能実装者から、組織のデータを守る信頼性の高いセキュリティアーキテクトへと、その役割をさらに高めることができるのです。

コメント