A. transaction security policies で実際にやらかした判断
今でも思い出すと胃が痛くなるプロジェクトがある。Salesforceコンサルタントとして、私はその顧客のセキュリティ要件に真っ向からぶつかることになった。要件は非常にシンプルに見えた。「特定の機密レポートを、社外IPからのアクセスではダウンロードさせない」というものだ。ただし、レポート自体は表示できる必要がある、という縛りがあった。
当時の私は、よし、これはtransaction security policiesの出番だと即座に判断した。`ReportEvent` を監視し、特定のユーザープロファイルが関与し、かつ `SourceIp` が許可された範囲外であれば `Block` アクションを発動させる、というシンプルなポリシーを想定していた。宣言的に設定できる部分が多かったため、すぐに解決できると見積もってしまったのだ。
// 当時考えたポリシーのイメージ(Enhanced Transaction Security Policyとして実装する前の構想)
// New Transaction Security Policy -> Event: ReportEvent
// Condition:
// ReportEvent.APIName starts with 'Confidential_Report_' AND
// ReportEvent.SourceIp not in ('192.168.1.0/24', '10.0.0.0/8') AND
// ReportEvent.User.Profile.Name = 'Confidential Report User Profile'
// Action: Block
// Notification: Notify user
これが最初の大失敗だった。
レポートダウンロード時のブロックポリシー、その複雑な罠
最初はその単純なポリシーで動き出した。社内からはダウンロードでき、社外からはブロックされる。顧客も満足しているように見えた。しかし、運用が始まって数ヶ月後、問題が次々と浮上し始めた。1. 複雑化する条件とLambda関数の導入
顧客のビジネス要件は常に変化する。「特定の部署のユーザーは、たとえ社外からでも承認されたデバイスからならダウンロードを許可したい」「しかし、そのデバイスは特定の証明書を持っている必要がある」「特定のIP範囲からのアクセスは、たとえ機密レポートでも無条件で許可」など、当初のシンプルだった条件が雪だるま式に増えていった。 宣言的なポリシー設定では対応しきれなくなり、最終的には **Enhanced Transaction Security Policy** と呼ばれる、Apexコードでロジックを記述する方式に切り替えることになった。これにより、Lambda関数を用いてAWS上で独自のロジックを実行できるようになったわけだが、この選択が更なる後悔を生むこととなる。
// 当時実装したLambda関数の抜粋イメージ(Apex版)
public class ConfidentialReportPolicy implements TxnSecurity.PolicyCondition {
public boolean evaluate(TxnSecurity.Event e) {
ReportEvent rEvent = (ReportEvent) e;
// まず基本的な条件
if (!rEvent.APIName.startsWith('Confidential_Report_')) {
return false; // 対象外レポート
}
// IPアドレスの判定
// ここで顧客要件に応じて、許可IPリスト、ブロックIPリスト、動的なIPグループなどを参照
// 例: Custom SettingやCustom Metadata Typeから許可IP範囲を取得
Set<String> allowedIps = MySettings__c.getInstance().AllowedIPs__c.split(',').asSet(); // 仮
String sourceIp = rEvent.SourceIp;
// ... IP範囲判定ロジック ...
if (!isIpAllowed(sourceIp, allowedIps)) {
// 社外IPからのアクセスをブロックしたい
// ただし、特定のユーザーグループは例外...
// ここでさらにユーザープロファイル、権限セット、カスタム権限のチェックが加わる
User u = [SELECT Profile.Name, HasPermissionSet__c FROM User WHERE Id = :rEvent.UserId LIMIT 1];
if (u.Profile.Name == 'Special_Profile' && u.HasPermissionSet__c) {
return false; // 特例ユーザーは許可
}
return true; // ブロック
}
return false; // それ以外は許可
}
private boolean isIpAllowed(String ip, Set<String> allowedIps) {
// 実際にはCIDR対応など複雑なロジックが必要になる
// 今ならNetwork.isIpInNetwork()のようなユーティリティを使うが、当時は手実装も多かった
return allowedIps.contains(ip); // 仮実装
}
}
このApexクラスは、最終的に数十行にも及ぶ複雑な条件分岐を持つことになった。
2. パフォーマンスとGovernor Limitsの罠
Enhanced Transaction Security Policy は同期実行される。つまり、イベントが発生するたびにApexコードが呼び出され、その結果に基づいてアクションが実行される。ユーザー数が多く、機密レポートのダウンロード頻度もそれなりにある環境で、複雑なApexコードを実行させ続けるのは、SalesforceのGovernor Limitsとの戦いだった。 特に、Apexコード内でSOQLクエリを発行してユーザー情報やカスタム設定を参照するたびに、CPU時間やクエリ数の制限にじわじわと近づいていく。最初は問題なかったものの、ユーザーが増え、イベント発生頻度が上がると、たまにタイムアウトするケースが出始めた。「特定のダウンロードリクエストが非常に遅延する」という報告が増え、調査するとポリシーの評価に時間がかかっていることが判明した。 この時、「本当にこのセキュリティ要件は、Salesforceのトランザクションセキュリティポリシーで解決すべきだったのか?」という疑問が初めて頭をよぎった。3. メンテナンスコストとテストの悪夢
Lambda関数(Apexコード)でポリシーを管理するということは、通常の開発プロセスに乗せる必要があるということだ。要件が変わるたびにコードを修正し、テストクラスを書き、サンドボックスでテストし、本番環境にデプロイする。このサイクルが、セキュリティポリシーという「動的に変わりやすい」要件に対して、非常に重い負担となった。 特に、複数の条件が絡み合う複雑なポリシーでは、網羅的なテストケースを作成するのが非常に困難だった。ある条件を追加すると、別の既存の条件が意図せず回避されてしまう、といったバグも発生し、顧客からの信頼を損ねる原因にもなった。 当時は、Declarative (Standard) Transaction Security Policies の機能がまだそこまで洗練されていなかったり、特定の条件(例えば複雑なIP範囲判定)をカバーしきれなかったりする部分があったため、仕方なくEnhanced Policyを選んだ経緯がある。しかし、その後のメンテナンスコストを考えると、もっと初期段階で顧客と「この要件は本当にSalesforce単体で達成すべきなのか?」「もっとシンプルなルールにできないのか?」と議論すべきだったと心底後悔している。今なら別の選択をする
もし、今同じ要件が提示されたら、私は transaction security policies を最終手段として検討し、まず他の方法を模索するだろう。まず、情報漏洩対策という観点から、Salesforceの機能範囲を超えた**DLP (Data Loss Prevention) ソリューション**や**CASB (Cloud Access Security Broker) ソリューション**の導入を顧客に強く推奨する。Salesforce単体で実現できることには限界があり、画面のスクリーンショットやコピー&ペースト、PCへのダウンロード後のデータ移動など、あらゆる漏洩経路をカバーするためには、より包括的なセキュリティ基盤が必要だということを説明する。
それでもSalesforceの機能で解決したいというのであれば、transaction security policies の `Block` アクションではない、**予防的なアプローチ**を優先する。
- レポートフォルダの共有設定: レポート自体を閲覧させたくないユーザーには、フォルダレベルでアクセスを制御する。
- 権限セットによるレポートタイプへのアクセス制御: ダウンロードさせたくない機密データを含むレポートタイプに対して、アクセス権限を与えない。
- カスタム権限とApexによるダウンロードボタンの非表示: Lightning Experienceでは標準のレポートダウンロード機能の制御は難しいが、カスタムレポートビューアを作成し、ダウンロードボタンをApexで制御することは可能かもしれない。この場合、権限セットとカスタム権限を使って、ダウンロードボタンの表示/非表示を切り替える。
- `Notify` または `Enforce` アクションの活用: どうしても `ReportEvent` を監視する必要があるなら、まずは `Block` ではなく `Notify` (管理者に通知) や `Enforce` (セッションを終了) から始める。これにより、ユーザーへの影響を最小限に抑えつつ、異常を検知できる。`Block` は最終手段として、本当にビジネスに壊滅的な影響を与えるような場合にのみ使用すべきだと学んだ。
コメント
コメントを投稿