MIXI MにおけるEMV 3-D Secure対応とElixirによるACS内製開発
開発本部MIXI M事業部の@k-asmです。
MIXI Mでは、2023年10月30日にJCBブランドのプリペイドカードでEMV 3-D Secure(3-D Secure 2.0)に対応しました。
Visaブランドのプリペイドカードは、外部の3-D Secureサービスを利用することで、すでに3-D Secureに対応していました。今回JCBブランドで3-D Secure対応するにあたり、MIXI Mでは3-D Secureサービスの1つであるACSを内製開発しました。
以前「MIXI MにおけるPCI 3DS準拠のための道のり」という記事を書きました。本記事はその続きとして、MIXI MにおけるACS開発について書いていきます。
なおMIXI Mとは、認証から決済までをワンストップで提供できる基盤システム & WALLETサービスです。主にMIXI社の提供するサービス向けに決済基盤を提供しているのと同時に、プリペイドカードを発行して日々の決済に利用できます。
目次
1. 3-D Secureとは何か
2. 3DSを利用した決済のデータフロー
∘ ACSとは何か
3. ACS内製開発について
4. MM ACS開発に伴う主な技術的トピック
∘ カード会員データと3DS機密データの取り扱い
∘ メッセージのパラメータバリデーション
∘ App-basedフローにおけるJWEとJWSの生成
∘ 取引タイムアウトの実装
5. MIXI Mにおける今後の3DS対応
6. おわりに
7. 参考文献
3-D Secureとは何か
3-D Secure(3DS)は、カードを利用したオンライン決済のセキュリティを強化するためのメッセージングプロトコルです。
3DSを利用しない取引の場合、カード所有者(カードホルダー)がカード番号や有効期限などを入力して決済すると、カード発行者(イシュアー)に対してオーソリゼーション(オーソリ)と呼ばれる事前承認プロセスが行われます。オーソリが承認されると、ECサイトなどの加盟店では決済が完了したとみなされます。
3DSを利用した決済のデータフロー
以下の図は3DSを利用しない取引におけるオーソリのデータフローです。加盟店は決済ネットワークを通じてオーソリをリクエストし、イシュアーから返却されたオーソリの結果に応じて、カードホルダーに商品を提供するか判断します。
3DSはオーソリの前に行われ、本人認証することでカードの不正利用を防止します。3DSでは加盟店で決済しようとするクライアントがカードホルダー自身であるかを、カードホルダーがイシュアーに登録しているパスワードなどの本人認証で確認します。
以下の図は3DSを利用する取引のデータフローです。加盟店はオーソリのリクエストに先んじて、3DSシステムに対して3DS認証を要求します。3DSシステムは必要に応じてカードホルダーと本人認証し、3DS認証の結果を加盟店に返します。加盟店は3DS認証の結果に応じて、オーソリの要求をするか判断します。
この図では3DSシステムが1つのサービスであるかのように書かれていますが、実際は3DS Server¹, Directory Server, Access Control Server(ACS)という三種類のシステムが協調して3DS認証します。
また、EMV 3DSではリスクベース認証によって決済のリスクを判定し、低リスクと判定された時はカードホルダーによる本人認証をスキップできます。これをFrictionlessフローと呼び、ユーザーに対してシームレスな決済体験を提供できます。
[1]: 3-D Secure 1.0ではMPIと呼ばれていました。
ACSとは何か
3DSでは加盟店・決済ネットワーク・イシュアーの三者が協調して本人認証をします。このうちイシュアーの立場でシステムを提供するのがAccess Control Server(ACS)と呼ばれるシステムです。ACSはイシュアーと連携して、カードホルダーとの実際の本人認証プロセスを提供します。またリスクベース認証することで、Frictionlessフローに進むかChallengeフロー²に進むか判定します。
MIXI Mはプリペイドカードを発行するイシュアーなので、ACSを導入することで、3DS対応のEC加盟店でMIXI Mプリペイドカードを使った3DS取引が可能となります。
[2]: カードホルダーに対して本人認証インタラクションを求めるフロー。
ACS内製開発について
イシュアーが3DS対応する場合、外部サービスのACSを利用するのが一般的です。前述した通り、MIXI MではすでにVisaブランドで外部のACSサービスを利用しています。しかし、ACSの利用料が無視できないほど大きいことが運用上問題となっており、コスト削減の一環として内製で開発する決定をしました。³
ACS内製開発は次のような順序で進めました。
ACSの仕様はクレジットカードのセキュリティ規格を開発するための国際的な組織であるEMVCoにより「EMV® 3-D Secure Protocol and Core Functions Specification」として公開されています。この仕様に沿って開発した後、EMVCoによる認証取得を行いました。MIXI MではMM ACSという製品名で認証を取得しています。
続いて、カードブランドと接続するためにPCI 3DSの準拠認定を受けました。このPCI 3DS準拠認定に関しては「MIXI MにおけるPCI 3DS準拠のための道のり」をご覧ください。
最後にカードブランドと接続し、ACSの本番運用を開始します。カードブランド接続の過程で、ブランドによる認定が行われることもあります。MM ACSはJCBブランドと接続するにあたり、J/Secure™認定Operatorとして認定されています。
[3]: 2023年11月現在ではJCBブランドのみ内製ACSを利用していますが、将来的にVisaブランドも内製ACSに切り替える予定です。
MM ACS開発に伴う主な技術的トピック
それでは、内製ACSであるMM ACSを開発するにあたって解決した技術的トピックを紹介していきます。MM ACSでは開発言語としてElixirを採用したため、Elixirのサンプルコードを示しながら説明していきます。
カード会員データと3DS機密データの取り扱い
3DSの通信フローで流れるメッセージには、決済しようとしているカード番号や有効期限、カード会員名といったカード会員データが含まれます。このカード会員データはPCI DSSで保護対象となっているデータであり、保存する際には暗号化を行うなど、取り扱いには注意を要します。
また、3DSのメッセージ中には、カード会員データの他にもPCI 3DSで定められている3DS機密データが含まれています。3DS機密データの中には保存が許可されていないフィールドが存在しているため、こちらも取り扱いには注意を要します。
カード会員データの取り扱い
MIXI Mはすでにイシュアーとしてプリペイドカードを発行しており、プリペイドカードのカード会員データがPCI DSSの基準で安全に保存されています。そのため、MM ACSでもMIXI Mの既存の仕組みを利用してカード会員データを取り扱っています。
MIXI MではAWS KMSを利用してカード会員データの暗号化と保存をしています。
まず、KMSのGenerateDataKey APIを呼び出して、データキーの生成と、CMKと呼ばれるKMSのマスターキーを利用してデータキーを暗号化した値を生成します。
{encrypted_key, plaintext_key} = Kms.generate_data_key()
続いて、平文のデータキーを利用して、カード会員データを暗号化します。AES256で暗号化するサンプルコードが以下です。暗号化したいデータをplaintext
として、及びAADとIVを合わせて渡しています。
{encrypted_data, tag} = :crypto.crypto_one_time_aead(:aes_256_gcm, plaintext_key, iv, plaintext, aad, true)
平文のデータキーは保存せず破棄し、暗号化されたカード会員データ(encrypted_data
)と暗号化されたデータキー(encrypted_key
)、その他復号に必要な情報(iv
, tag
)をデータベースに保存します。
このように、データごとに異なる鍵を利用して暗号化を行っています。またデータキーはデータベースに、データキーを暗号化するCMKはKMSに保存されており、それぞれ別の場所に保存されています。
復号は、まず暗号化されたデータキーをKMSのDecrypt APIで復号するところから始めます。
plaintext_key = Kms.decrypt(encrypted_key)
復号されたデータキーを使って、暗号化されたカード会員データを復号します。
plaintext = :crypto.crypto_one_time_aead(:aes_256_gcm, plaintext_key, iv, encrypted_data, aad, tag, false)
復号されたカード会員データは利用が終わり次第、保存せず破棄します。
3DS機密データの取り扱い
3DS機密データは保存する際に暗号化を行いますが、保存が許可されていないフィールドは保存する前にトランケートしています。トランケート処理に加えて、データベース保存する時は、保存が許可されていないフィールドが含まれていないことを確認するバリデーションを行っています。
機密データのバリデーションはParams.SchemaとEcto.Changesetを利用して実装しました。以下にサンプルコードを示します。
このサンプルコードでは、SensitiveData.validate/1
関数で保存が許可されていない機密データの存在確認を行っています。データベース保存する前にvalidate_params/1
関数でバリデーションを行い、モデルのパラメータバリデーションを行うのと同時にSensitiveData.validate/1
関数を呼び出します。この時点で、保存が許可されていない機密データが残っていた場合はエラーとなります。
MM ACSではElixirのマクロ構文を使ってボイラープレートをマクロ定義しています。これにより、モデルの数が増えた時にバリデーションが省かれるリスクを減らします。
メッセージのパラメータバリデーション
ACSは、主にJSONペイロードによるメッセージのやりとりを行うことで、他3DSシステムと協調して3DS認証をします。このメッセージのパラメータは、仕様で以下の要件が定められています。
- 値の長さ
- 値のフォーマット
- 値の内容
- 認められるDevice Channel
・ Device Channelは3DS認証トランザクションの発信元を表しており、APP, BRW, 3RIの3つが定義されています。
・ 発信元の種類ごとに異なるフローで3DS認証が行われます。それぞれApp-based, Browser-based, 3RIというフローです。
・ 日本の加盟店でよく見かける3DS認証フローはBrowser-basedですが、一部App-basedで決済する加盟店も存在します。 - 認められるMessage Category
・ PAとNPAの2種類があります。
・ PAはEC決済のカードホルダー認証、NPAは決済情報に紐付かない本人確認に利用される、と定義されています。 - 認められるメッセージの種類
・ 3DS認証は処理のフェーズに応じて異なるメッセージが利用され、それぞれ仕様上認められるパラメータが異なります。 - パラメータの存在条件
・ Required, Conditional, Optionalの3段階あり、Requiredのパラメータは必ず含まれることが求められます。
詳しく知りたい方は、仕様書である「EMV® 3-D Secure Protocol and Core Functions Specification」の「3-D Secure Data Elements」の章をご覧ください。
メッセージを受けた3DSシステムは、要件に従ってパラメータのバリデーションを行い、有効なメッセージでない場合は、仕様で定義されたエラーを返す必要があります。
なお、EMV 3DSには現在2.1.0, 2.2.0, 2.3.1の3種類の有効なバージョンが存在します。バージョンごとに上記パラメータの仕様は少しずつ異なっているため、バージョンを考慮したバリデーションが必要となります。
Elixirによるパラメータバリデーション
Elixirでは言語仕様としてパターンマッチが利用できます。特にこういった複雑なバリデーションを行う時には、強力な構文として利用できます。
例えばMap型の変数であるmessage
を、messageVersion
キーの値が2.1.0
と2.2.0
と 2.3.1
の時それぞれで処理を分岐させたいとします。if文やcase文、cond文といった条件分岐で実装可能なのですが、パターンマッチを使うことで以下のように書けます。
特に分岐条件が複雑になるとパターンマッチは強力な構文となり、実装のリーダビリティを大きく向上させられます。
バリデーションを行うモジュールではEcto.Changesetを全面的に採用しました。Ecto.Changeset
ではvalidate_required
やvalidate_length
のような基本的なバリデーションセットが用意されていることに加え、validate_change
を使うことでカスタムバリデータの定義を簡易な構文で行うことができます。
例えば、string型のdeviceChannel
とmessageVersion
の2つのパラメータを持つMessage
を定義します。deviceChannel
は01
, 02
, 03
のいずれかの値であることが求められ、messageVersion
のバリデータはValidator
モジュールのvalidate_message_version/1
に切り出したとします。このMessage
を定義するサンプルコードは以下のように書けます。
Message
をバリデーションしたい時は以下のように書けます。
MM ACSではElixirのパターンマッチの恩恵を受けて、複雑なバリデーション仕様を比較的読みやすい実装に落とし込むことができました。
App-basedフローにおけるJWEとJWSの生成
EMVCoで定義されている3DS認証フローにはApp-basedフロー、Browser-basedフロー、3RIの3種類が存在します。App-basedフローは加盟店が3DSのモバイルSDKを利用して3DS認証するフロー、Browser-basedフローはウェブブラウザを利用して3DS認証するフロー、3RIはカードホルダーが介在しない3DS認証フローです。
App-basedフローでは、モバイルSDKとACSが直接メッセージをやりとりする時、JSON Web Encryption(JWE)を用いてメッセージの暗号化を行います。JWEの暗号化・復号のための鍵はECDH-ESでSDKとACSの間に共有され、ECDH-ESの公開鍵はJSON Web Signature(JWS)で受け渡しが行われます。
PCI 3DSの要件では、このJWSを署名するための秘密鍵は、一定の要件を満たしたHSMで生成・管理することが求められます。詳しくは「MIXI MにおけるPCI 3DS準拠のための道のり」をご覧ください。MM ACSではAWS KMSを使うことで、この要件を容易に解決できました。
ECDH-ESで共有鍵を生成する時は、ECDHでシークレットを共有したうえで、Concat KDFで派生キーを生成します。ECDHによる共有シークレットの生成やConcat KDFによる派生キーの生成はJOSEを利用して行いました。以下にサンプルコードを示します。
派生キーの生成はErlangのモジュールを利用していますが、このような十分に枯れたErlangのエコシステムに乗って開発できることはElixirの大きな利点です。
取引タイムアウトの実装
ACSは3DS認証が何らかの理由で一定時間内に完了しなかった場合、認証トランザクションをタイムアウトさせる必要があります。タイムアウト処理は3DS認証の処理リクエストとは別に非同期的に行うため、ACSは何らかの方法で非同期処理を実装する必要があります。
Elixirによる非同期処理Workerの実装
MM ACSではAmazon SQSとElixirのSupervisorを使って非同期処理Workerを実装しています。
非同期処理の起点となる3DS認証リクエストが行われた際に、タイムアウトの時間をDelaySeconds
に設定した遅延キューをEnqueueします。タイムアウトの時間が経過した後、非同期処理Workerは遅延キューをDequeueし、必要ならば当該トランザクションのタイムアウト処理を行います。
Supervisor
はElixirによる並行処理の基盤モジュールです。子プロセスのライフサイクルを管理し、必要に応じてリスタートさせる機能を提供します。これによりエラーが発生した時にシステム全体をクラッシュさせるのではなく、問題のある部分だけをリセットして、システムを正常な状態に保つことを可能にします。
Supervisor
に非同期処理を行うWorkerプロセスを管理させることで、他プロセスで行われている処理とは独立して非同期処理が安定実行されることを担保します。
WorkerプロセスはGenServer
を使って実装できます。GenServer
は汎用的なサーバプロセスを実装するために提供されているモジュールです。Workerプロセスの起点となるモジュールは、例えば以下のように書けます。
この非同期処理Workerは以下のようにSupervisor
に管理させられます。
Supervisor
はErlang OTPフレームワークの一部として動作し、簡潔なコードでErlang VMの安定性・耐障害性の恩恵を受けることができます。
MIXI Mにおける今後の3DS対応
MIXI Mでは今後、JCBブランドに続いてVisaブランドにもMM ACSの導入を予定しています。また、加盟店の立場で3DSシステムを提供する3DS Serverの内製開発も完了しているため、こちらも順次内製実装に置き換える予定です。
EMV 3DSの重要な特徴の1つはリスクベース認証が可能な点です。しかし、現在のMM ACSでは有効なリスクベース認証が実装できていません。こちらは重要な課題として今後実装予定です。
ACS内製化の利点として柔軟に改善していける点があります。今後も継続的に開発と改善を実施していきます。
おわりに
MIXI Mでは3DS認証基盤の他にも、国際的な決済ネットワークへの接続システム、プリペイドカードのオーソリゼーションやカードの売上を精算するシステムなど、様々な決済基盤を内製で開発しています。そのほとんどのシステムでElixirを採用しており、本記事で挙げたようなパターンマッチやErlang VM, Erlang/OTPの恩恵を大いに受けています。
現在MIXI Mではサーバエンジニアを採用募集しています。Elixirでの開発や決済基盤、ID基盤の開発に興味がある方は、是非カジュアル面談でお待ちしています。