役割:Salesforce アーキテクト
背景と適用シナリオ
現代のビジネスにおいて、クレジットカード決済は不可欠な要素です。しかし、クレジットカード情報を扱うことは、厳格なセキュリティ基準への準拠を意味します。それが、PCI DSS (Payment Card Industry Data Security Standard)、日本語で「ペイメントカード業界データセキュリティ基準」と呼ばれるものです。これは、カード会員データを保護し、クレジットカード詐欺を減らすことを目的として策定された一連の技術的および運用的要件です。
Salesforce は、顧客関係管理 (CRM) の中心として、商談、注文、請求情報など、決済プロセスに関連する多くのデータを扱います。そのため、Salesforce プラットフォーム上で決済ソリューションを設計・構築する Salesforce アーキテクトにとって、PCI コンプライアンスは避けて通れない最重要課題となります。
具体的な適用シナリオとしては、以下のようなケースが考えられます。
- Eコマースプラットフォームとの連携: Salesforce Commerce Cloud や他の Eコマースサイトからの注文情報と決済結果(ただし、カード番号そのものではない)を Salesforce に連携し、顧客サービスやマーケティングに活用する。
- 請求・支払いポータル: Experience Cloud (旧 Community Cloud) を利用して、顧客が自身の請求書を確認し、支払いを行えるポータルサイトを構築する。
- 電話受注システム: コンタクトセンターのオペレーターが Service Cloud を利用して顧客から電話で注文を受け付け、決済処理を行う。
これらのシナリオにおいて、もしクレジットカードの生情報(カード番号、有効期限、セキュリティコードなど)を Salesforce プラットフォーム内で直接「保存、処理、または伝送」してしまうと、Salesforce 環境全体が PCI DSS の監査対象(スコープ)に含まれてしまいます。これは、監査コストの増大、セキュリティ管理の複雑化、そして万が一のデータ漏洩時における甚大なビジネスリスクを招きます。
したがって、Salesforce アーキテクトの使命は、ビジネス要件を満たしつつ、Salesforce を PCI DSS のスコープから意図的に「除外(De-scope)」するアーキテクチャを設計することにあります。本稿では、そのための原理原則とベストプラクティスについて、アーキテクトの視点から詳述します。
原理説明
Salesforce 環境における PCI コンプライアンスを達成するためのアーキテクチャ設計の核心は、「Salesforce プラットフォーム上でカード会員データ (Cardholder Data - CHD) を直接扱わない」という一点に尽きます。これを実現するための主要な技術と考え方がいくつか存在します。
1. Salesforce のスコープアウト (De-scoping)
最も重要な原則は、Salesforce を PCI DSS の監査範囲から外すことです。Salesforce 自体は、インフラストラクチャレベルで PCI DSS Level 1 サービスプロバイダとして認定されています。しかし、これは Salesforce が提供する「箱」が安全であるというだけであり、その「箱」の中に何を入れるかは利用者の責任です。これを責任共有モデル (Shared Responsibility Model) と呼びます。
アーキテクトとして、私たちは CHD が Salesforce のサーバーを通過したり、データベースに保存されたりすることが一切ないようなデータフローを設計しなければなりません。ユーザーのブラウザから入力されたクレジットカード情報は、Salesforce を経由せず、直接 PCI 準拠のペイメントゲートウェイに送信されるべきです。
2. トークン化 (Tokenization)
スコープアウトを実現するための最も強力な技術がトークン化 (Tokenization) です。トークン化とは、機密性の高い CHD を、トークンと呼ばれる一意で機密性のない代替値に置き換えるプロセスです。
- 顧客がブラウザのフォームにクレジットカード情報を入力します。
- このフォーム(通常はペイメントゲートウェイが提供する iFrame や JavaScript ライブラリを使用)は、入力された情報を Salesforce のサーバーではなく、直接ペイメントゲートウェイに送信します。
- ペイメントゲートウェイは、受信した CHD を自身の安全な保管庫 (Vault) に保存し、それに対応する一意のトークン(例:`tok_1Ld...`)を生成します。
- このトークンだけが、顧客のブラウザ経由で Salesforce に返されます。
- Salesforce は、このトークンを顧客情報や注文情報に関連付けて保存します。このトークン自体には価値がなく、それ単体では元のカード番号を推測することはできません。
以降の決済(継続課金や再決済など)では、Salesforce は保存されたトークンをペイメントゲートウェイに送信するだけで、実際の決済処理を依頼できます。これにより、Salesforce は CHD に一切触れることなく、決済機能を実現できるのです。
3. ペイメントゲートウェイの統合パターン
アーキテクトは、ペイメントゲートウェイとの統合方法を慎重に選択する必要があります。
- iFrame / Hosted Payment Pages: 最も安全で推奨されるパターンです。決済フォーム部分をペイメントゲートウェイが提供する iFrame で埋め込むか、決済ページ自体にリダイレクトします。この方法では、ユーザーのブラウザとペイメントゲートウェイが直接通信するため、CHD が Salesforce の Web サーバーやアプリケーションサーバーに触れることはありません。
- Client-side JavaScript Library: ペイメントゲートウェイが提供する JavaScript ライブラリを Visualforce ページや Lightning Web Component に組み込む方法です。これも iFrame と同様に、クライアントサイド(ブラウザ)で CHD をトークンに変換し、Salesforce にはトークンのみが送信されるため、安全なパターンです。
- Direct API Integration (非推奨): Salesforce の Apex からペイメントゲートウェイの API を直接呼び出して CHD を送信するパターンです。これは、CHD が Salesforce のアプリケーションサーバーを通過するため、Salesforce 環境全体が PCI スコープに入ってしまいます。このパターンは絶対に避けるべきです。
4. Salesforce Shield の役割
Salesforce Shield は、Platform Encryption, Event Monitoring, Field Audit Trail から構成される高度なセキュリティ機能群です。ここで重要なのは、Shield の Platform Encryption を使ってカード番号を暗号化しても、PCI コンプライアンス要件を満たしたことにはならないという点です。PCI DSS は、保存時の暗号化だけでなく、伝送経路の保護や厳格な鍵管理など、多岐にわたる要件を課しています。
しかし、Shield は PCI コンプライアンスを補強する補完的統制 (Compensating Control) として非常に有効です。例えば、決済トークンや個人情報(PII)など、CHD 以外の機密データを Platform Encryption で暗号化したり、Event Monitoring でそれらのデータへの不審なアクセスを監視したりすることで、全体のセキュリティ体制を強化し、監査人に対して堅牢なデータ保護への取り組みを示すことができます。
サンプルコード
前述の通り、Salesforce 内で直接 CHD を扱うコードを書くべきではありません。以下のサンプルは、ペイメントゲートウェイからトークンを受け取った後、そのトークンを使って Apex コントローラーから決済処理を依頼する(Callout を行う)というシナリオを想定したものです。このコードは、機密情報であるカード番号ではなく、安全なトークンのみを扱っている点に注意してください。
この例では、Named Credential を使用してエンドポイント URL と認証情報を安全に管理しています。これはセキュリティのベストプラクティスです。
Apex Controller (PaymentController.cls)
// public with sharing を指定し、オブジェクトの共有ルールを強制します。 public with sharing class PaymentController { /** * @description Lightning Web Component から呼び出され、決済処理を実行するメソッド * @param orderId 処理対象の注文ID * @param paymentToken ペイメントゲートウェイから取得した非機密の決済トークン * @param amount 決済金額 * @return String 決済結果のトランザクションID */ @AuraEnabled public static String processPayment(Id orderId, String paymentToken, Decimal amount) { // 入力値の検証 (簡易的な例) if (String.isBlank(paymentToken) || amount <= 0) { throw new AuraHandledException('Invalid payment information provided.'); } // 実際のペイメントゲートウェイのエンドポイントとリクエストボディは // 利用するサービスの仕様に従います。 // ここでは "Payment_Gateway_API" という名前の Named Credential を使用しています。 HttpRequest req = new HttpRequest(); req.setEndpoint('callout:Payment_Gateway_API/v1/charges'); req.setMethod('POST'); req.setHeader('Content-Type', 'application/json;charset=UTF-8'); // idempotencyキーを設定し、二重支払いを防ぐのがベストプラクティス req.setHeader('Idempotency-Key', generateIdempotencyKey(orderId)); // リクエストボディを作成します。 // ★重要★: ここではクレジットカード番号のような機密情報は一切含めず、 // 安全なトークンのみを送信します。 Map<String, Object> requestBodyMap = new Map<String, Object>{ 'amount' => amount * 100, // 金額はセント単位で指定することが多い 'currency' => 'jpy', 'source' => paymentToken, // ペイメントゲートウェイから取得したトークン 'description' => 'Charge for Order ID: ' + orderId }; req.setBody(JSON.serialize(requestBodyMap)); Http http = new Http(); String transactionId = null; try { HttpResponse res = http.send(req); // 応答を処理します if (res.getStatusCode() == 200 || res.getStatusCode() == 201) { Map<String, Object> responseBody = (Map<String, Object>) JSON.deserializeUntyped(res.getBody()); transactionId = (String) responseBody.get('id'); // 成功した場合、注文レコードを更新するなどの後続処理 updateOrderStatus(orderId, transactionId, 'Paid'); } else { // エラーハンドリング System.debug('Payment failed. Status: ' + res.getStatus() + ' Body: ' + res.getBody()); updateOrderStatus(orderId, null, 'Payment Failed'); throw new AuraHandledException('Payment processing failed. Please try again.'); } } catch (Exception e) { // コールアウトの例外処理 System.debug('Callout exception: ' + e.getMessage()); updateOrderStatus(orderId, null, 'Payment Failed'); throw new AuraHandledException('An error occurred while connecting to the payment service.'); } return transactionId; } /** * @description べき等性を確保するためのユニークなキーを生成します。 * @param recordId レコードID * @return String べき等キー */ private static String generateIdempotencyKey(Id recordId) { // より堅牢なユニークキー生成ロジックを実装することが望ましい return String.valueOf(recordId) + '_' + System.now().getTime(); } /** * @description 注文のステータスを更新するヘルパーメソッド * @param orderId 注文ID * @param transactionId トランザクションID * @param status 更新後のステータス */ @TestVisible private static void updateOrderStatus(Id orderId, String transactionId, String status) { // DML処理におけるセキュリティを考慮し、項目レベルセキュリティ(FLS)をチェック if (!Schema.sObjectType.Order.fields.Status.isUpdateable() || !Schema.sObjectType.Order.fields.Transaction_Id__c.isUpdateable()) { throw new AuraHandledException('Insufficient permissions to update order status.'); } try { Order ord = new Order( Id = orderId, Status = status, Transaction_Id__c = transactionId // カスタム項目 ); update ord; } catch (DmlException e) { System.debug('Failed to update order status: ' + e.getMessage()); // 適切なエラーロギング処理をここに追加 } } }
⚠️ 未找到官方文档支持: このコードは、特定のペイメントゲートウェイ API を想定した概念的なサンプルです。実際の API エンドポイント、リクエスト/レスポンス形式、認証方法は、利用するペイメントゲートウェイ(Stripe, Braintree, PayPal など)の公式ドキュメントに厳密に従う必要があります。ただし、Apex での Callout の作成、JSON のシリアライズ/デシリアライズ、Named Credential の利用方法は、Salesforce の公式ドキュメントに準拠しています。
注意事項
アーキテクトとして、PCI 準拠のソリューションを設計・維持する上で、以下の点に細心の注意を払う必要があります。
- スコープクリープ (Scope Creep) の防止: 最大のリスクは、意図せず CHD が Salesforce に混入することです。例えば、「メール-to-ケース」機能で顧客がメール本文にカード番号を記載して送信したり、Chatter やカスタムオブジェクトのテキスト項目にユーザーが手で入力したりするケースが考えられます。データマスキングツールや入力規則、定期的なデータ監査を通じて、CHD がプラットフォーム上に存在しないことを保証する仕組みが必要です。
- 厳格な権限管理: たとえ決済トークンであっても、誰でもアクセスできるべきではありません。プロファイル、権限セット、項目レベルセキュリティ (Field-Level Security) を用いて、トークンが保存されている項目へのアクセスを、決済処理に本当に必要なユーザーやシステム連携ユーザーのみに限定する「最小権限の原則」を徹底してください。
- インテグレーションのセキュリティ: ペイメントゲートウェイへの Callout は、必ず Named Credential を使用してエンドポイントと認証情報を保護してください。コード内に認証情報をハードコーディングすることは絶対に避けるべきです。また、通信はすべて TLS 1.2 以上で暗号化されていることを確認します。
- デバッグログの取扱い: Apex のデバッグログには、リクエストボディやレスポンスボディの内容が出力される可能性があります。たとえトークンであっても、本番環境ではデバッグログのレベルを適切に管理し、機密情報がログに残らないように注意が必要です。`System.debug()` の内容も同様です。
- サードパーティ AppExchange アプリ: AppExchange から決済関連のアプリケーションをインストールする際は、そのアプリが PCI コンプライアンスにどのように対応しているか(トークン化を利用しているかなど)を必ずベンダーに確認し、ドキュメントを精査してください。
- 定期的なレビュー: 設計したアーキテクチャが、ビジネスの成長や要件の変更に伴い、意図せず PCI スコープに入っていないか、定期的に(少なくとも年1回)アーキテクチャレビューを実施することが重要です。
まとめとベストプラクティス
Salesforce アーキテクトが PCI 準拠の決済ソリューションを構築する上で、心に刻むべき最も重要な原則は「責任の分離」です。Salesforce は顧客関係を管理する最高のプラットフォームであり、ペイメントゲートウェイは決済情報を安全に処理する専門家です。それぞれの得意な領域に責任を集中させることが、安全でスケーラブルなアーキテクチャへの鍵となります。
以下に、アーキテクトとしてのベストプラクティスをまとめます。
- ゴールデンルールを遵守する: Salesforce プラットフォーム内で、クレジットカード会員データ (CHD) の「保存、処理、伝送」を絶対に、いかなる形でも行わない。
- トークン化を全面的に採用する: 信頼できる PCI DSS Level 1 準拠のペイメントゲートウェイを選定し、そのトークン化ソリューションを最大限に活用する。
- Salesforce を経由しないデータフローを設計する: iFrame や JavaScript ライブラリを利用し、ユーザーのブラウザからペイメントゲートウェイへ CHD が直接送信されるアーキテクチャを選択する。
- 堅牢なセキュリティモデルを実装する: 最小権限の原則に基づき、決済トークンや関連データへのアクセスを厳格に制御する。Salesforce Shield を補完的統制として活用し、データ保護と監視を強化する。
- 責任共有モデルを深く理解する: Salesforce が提供するインフラの安全性と、自身が構築するアプリケーションとデータの安全性は別物であることを常に意識し、顧客としての責任を果たす設計を行う。
- 継続的な監視と監査を行う: 一度構築して終わりではなく、定期的なセキュリティレビュー、脆弱性スキャン、データ監査を通じて、コンプライアンスが維持されていることを確認するプロセスを確立する。
これらの原則に従うことで、Salesforce の強力な CRM 機能を最大限に活用しながら、PCI コンプライアンスという複雑で重要な要件をクリアし、顧客に安全な決済体験を提供することが可能になります。アーキテクトの役割は、単に機能するシステムを構築するだけでなく、ビジネスをリスクから守り、信頼を築くセキュアなシステムを設計することにあるのです。
コメント
コメントを投稿