Merpay Tech Fest 2022 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2022年8月23日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。
この記事は、「メルカリグループの認証基盤における理想と現状、今後の取り組み」の書き起こしです。
メルカリグループの認証基盤における理想と現状、今後の取り組みについて@kokukumaが発表します。
自己紹介
僕は2019年頃にメルペイのIDPチームに入って以来、認証・認可に関する仕事をしています。この分野をはじめてから3年ほどになります。やってみるとかなり面白かったので今後10年くらいは続けていきたいです。
IDPチームの基本的な役割は、メルカリアカウントに関する認証・認可、トークンの管理です。
IDPチームの目的
IDPチームの目的は、メルカリグループのサービスに適切な認証・アクセス制御・Data Protectionを標準的な形で導入することです。この内容を、軽く説明します。
適切・標準的
まず、認証・アクセス制御・Data Protectionにおいて、どのような状況が適切だと考えているかについてです。認証については、必要十分に強固な認証を提供することが適切だと考えています。
どの程度の認証強度が必要になるかは、サービスがどういうものを提供しているかによって変わってきます。ユーザーにとっては、認証自体がそのサービスを使う目的ではないため、必要十分であればできる限りしたくないものです。よってプロダクトサイドから見ると、自分たちのサービスにとって必要十分に強固な認証をしたいという結論に至ります。
アクセス制御に関しては、最小権限の原則を適用することを目指しています。
余計な権限を付与しないことに加え、適切な粒度の権限を設定することも含まれます。たとえば、付与する権限の粒度が粗すぎると使う必要のない権限まで付与することになります。反対に粒度が細かすぎると、クライアントやお客さまにとって使いにくいサービスになってしまいます。
Data Protectionに関しては、データの最小化とUnlinkabilityを実現することが適切だと考えています。たとえば同じグループ内の複数のカンパニーがそれぞれ個人情報を保存していたとき、それらの情報を容易に紐付けられないようにするのがデータのUnlinkabilityに関わります。
これらの機能は基本的にどのサービスでも使うので、汎用性がかなり重要です。特定のチームやプロジェクト、ユースケースのみ提供するものではなく、できる限りどのチーム・プロジェクトでも利用できるようにすることがIDPの基本的なスタンスです。
一方で、これらの機能はセキュリティにおけるSingle Point of Failureになりやすいのが特徴です。そのため機能を作る際は、自分でゼロから仕様を考えるよりもできる限り標準仕様に沿った形で作っていくスタンスをとっています。
認証・認可に関わる機能を具体的にどのように位置付けしていくかは、どのようなクライアントを対象にしているかによって変わっていきます。
メルカリグループのサービス分類
メルカリのクライアントは、1st party Client、Relying Party、Resource Serverの三つにわけられると考えています。
この1st party Clientは、メルカリ/メルペイ自身が提供しているクライアントで、基本的にメルカリ/メルペイの機能はすべてそのクライアントを通して利用できます。
Relying Partyは、メルカリ/メルペイの一部の機能を利用して新しいサービスを作りたいときに使われます。
Resource Serverは、Relying Partyと同じくメルカリの一部の機能を使って新しいサービスを作るのが目的です。さらにそれをメルカリ/メルペイの機能の一部として提供し、かつメルカリ/メルペイとはセキュリティ的にもデータガバナンス的にも明確に境界を設けたい場合に利用します。
どのクライアントやコンポーネントを対象にするかによって、どのように適切な認証やアクセスコントロールを実現するかは変わります。IDPでやらなきゃいけないことは、それらの違いを意識しつつ、できる限り標準的な形に沿って機能を提供することです。
目次
今回の発表の中では、これらの中で1st party Clientにおける認証、Relying PartyとResource Serverにおけるアクセス制御やData Protectionについてお話しします。
1st party Clientにおける認証
まず、1st party Clientにおける認証の部分です。
どこの話か?
これは、メルカリ/メルペイ自身が提供する1st party Clientにおける認証の話です。
現状はどうなっているか
まずは、現状についてお話しします。
現在メルカリでサポートされている認証要素は、パスワードによる認証、電話番号によるSMS認証、パスコードによる認証、SNSログインによる認証の4種類です。ユーザーはそれらを使って、メルカリアカウントにログイン、あるいは追加認証をして別の機能を利用できます。
ここで追加認証と呼んでいるのは、お客さまが何かしらのクリティカルな操作を行う前に再度認証を実行することで、デバイスやアカウントの窃取の影響を軽減するためのものです。たとえばEメールの変更前にSMS認証を1回するような機能をいいます。現在の追加認証では、パスコードによる認証とSMS認証の2つをサポートしています。
去年6月くらいにフィッシング攻撃が増加し、SMS認証による追加認証が対策として導入されています。そのおかげで、現在のフィッシングの被害はかなり軽減されています。
課題
ただ、まだ課題は残っています。その一つが、ユーザー体験です。
フィッシング攻撃の対策として、いろいろな機能でSMS認証を要求するようになりました。攻撃者はフィッシングサイトでパスワードやSMSコードを取得してアカウントを乗っ取っています。ですが、SMS認証を追加認証として利用することによって、アカウントを乗っ取った後にも機能を使うにあたってSMSコードを再度取得しなければなりません。同じフィッシングサイト上で何度もSMSコードを取得するのはむずかしく、アカウントを乗っ取ったとしても機能を使えないため、乗っ取るモチベーションがなくなり結果的に攻撃を防げることができるのです。
一方で、お客さまもいろいろな機能においてSMS認証をしなければならないため、ユーザー体験があまりよくありません。
もう一つの課題は認証強度です。
パスワードやSMS認証など、現状サポートしている認証要素には、基本的にフィッシング耐性がありません。つまりこのフィッシングサイト経由でパスワードやSNSのコードなどの認証情報を攻撃者に接種される可能性がある状態です。
現状は、フィッシングサイトで何度も認証情報を取得するのが難しいことに依存して対策しています。ですが、今後攻撃のパターンが変わってきたときにも攻撃を防げるかはかなり怪しいです。フィッシング耐性のある認証要素を投入できていない点は、現状の大きな課題だと思います。
これが1st party Clientにおける認証の現状と課題です。
強い認証・phishing耐性がある認証 – FIDO
今後は、強い印象要素・phishing耐性がある認証要素としてFIDOの導入を進めていきたいと思います。
FIDOの好ましい特徴は、フィッシング耐性がある認証要素であることです。基本的な流れとしては、ユーザーが所持しているFIDO Authenticatorがあり、Authenticator内で管理する秘密鍵を使って認証します。
具体的には、事前にFIDO Authenticator内で管理される秘密鍵に対応する公開鍵をサーバーに登録し、認証時にいろいろな情報を集めて署名し、サーバーに送って署名を検証してユーザーを特定する流れです。
この署名の中には、どのドメインやウェブサイトで認証を実行したかという情報が含まれていて、それをサーバー側で検証できます。
そのため、仮にフィッシングサイトがFIDO認証を要求し、お客さまがそれに応えた結果攻撃者がアサーションを取得したとしても、メルカリ側ではどのサイトで認証が行われたかを検証できます。そのため、この認証方法はフィッシング耐性があると言われています。
ユーザーフレンドリーな認証要素であることも、好ましい特徴の一つです。よくあるのは、お客さまがtouchIDやFaceIDを使ってローカル認証し、FIDO Authenticatorが管理する秘密鍵にアクセスして、その秘密鍵で認証するという使い方です。お客さまは長いパスワードを入力、あるいはSNSのコードが届くのを待つ必要がないので、ユーザーフレンドリーな認証といえます。
それから、標準化されていることも今後お客さまが利用しやすくなるという意味で大きなポイントです。
一方で、秘密鍵の管理に関してはお客さまに大きな負担を強いることになります。たとえばお客さまはFIDO Authenticatorを紛失した場合、ログインや追加認証して実行する特定機能を利用できなくなります。
一番多い使われ方としては、スマートフォンをプラットAuthenticatorとして利用する形だと思います。そのため、お客さまが端末の機種変更をしたときに、一緒に古い端末を捨ててしまって、FIDO Authenticatorをロストするケースが考えられます。
これについて推奨されている対応方法は、複数のFIDO Authenticatorを事前に登録しておくことです。FIDO Authenticatorを一つ失っても、替わりを用意できる状態にしておきます。
ですが、セキュリティキーを持っている一般のお客さまはいないとしても、複数のデバイスを持っているお客さまは少数派だと思うので、機種変更前に新しい端末で鍵を登録し直してから古い端末を捨てるプロセスを取る必要があります。それに失敗した場合はFIDO Authenticatorをロストしてしまうので、CSに問い合わせをして本人確認をするなど何かしらのリカバリ手段を用意しておく必要もあります。
このような操作は今のメルカリ内では行われていないので、お客さまが慣れているものではないです。よって、お客さまにとってかなりの負担になるのではないかと考えています。一方で、負担を軽くしようとしてリカバリーのプロセスを簡単にするなどの対応をすると、今度はセキュリティ上の懸念が出てきてしまいます。
たとえばFIDO Authenticatorをロストしても、SMS認証などによって2つ目のFIDO Authenticatorを簡単に登録できるようにするとUXは改善されますが、攻撃者がアカウント乗っ取ったときに自分のFIDO Authenticatorを登録できるようになってしまう可能性が出てきます。登録やリカバリーのプロセスにおいて攻撃が可能な状態になってしまって、FIDOのフィッシング耐性を活かせなくなってしまうのです。
よって、現状において鍵の管理はお客さまの負担になってしまいます。ただ、FIDOのフィッシング耐性があるという特徴は今後利用したいですし、パスキーなどが一般的に利用できるようになれば対応方法も変わると思います。基本的にはFIDOを導入して、ログインや追加認証に徐々に適応していく予定です。
プロダクトそれぞれに合った柔軟な追加認証
FIDOの導入と一緒に進めたいのが、プロダクトそれぞれに合った柔軟な追加認証です。
FIDOを追加認証に利用できるようになるとすると、既存のSMS認証やパスコードによる追加認証との兼ね合いについての話題が持ち上がります。基本的な考え方としては、プロダクト次第です。
たとえば強いセキュリティを要求しているプロダクトであれば、FIDOでの追加認証しか許さないようにするでしょう。セキュリティ面ではSMS認証で十分だけどUXをよくするためにFIDOを使いたいプロダクトであれば、FIDOを使えるお客さまについてはFIDOの認証を、使えないお客さまについてはSMS認証を可能な状況にすると思います。よってIDPでは、どのような場合でも利用可能になるよう、機能を提供していく必要があります。
もう一つ、もし「お客さまがFIDO認証しているならフィッシングの可能性は低い」と判断できるなら、ログイン時に使った認証要素や他の機能で使った認証要素の情報を、今使おうとしている機能で参照できるようにしたいです。
たとえばログイン時にFIDO認証していたら、今使おうとしている機能において認証をスキップできるような仕組みを今後導入したいと思います。
以上が、1st party Clientにおける認証の話でした。
Relying Partyへのアクセス制御・Data Protection
次に、Relying Partyへのアクセス制御・Data Protectionの話をしていきたいと思います。
どこの話か?
これは、3rd partyのサービスがメルカリ/メルペイの機能を使って新しいサービスを作るときの話です。便宜上3rd partyと言いましたが、メルカリグループ内のサービスでもこの形を使って新しいサービスを作ることはあります。
ここでは、アクセストークンを取得した後にサーバー側でどのようにアクセス制御やData Protectionを考えているかを説明します。
現状はどうなっているか?
現状について説明するために、メルカリ/メルペイのサーバー内部の状況と、サービス間でどのようにリクエストが流れているかという説明をしたいと思います。
現状メルカリ/メルペイは、基本的にMicroserviceアーキテクチャで構成されているので、サーバー内部はMicroserviceがたくさんある状態です。各MicroserviceはそれぞれAPIを準備していて、他のMicroserviceに対してAPIを提供する形になっています。
ただAPIは、クライアントアプリケーションから直接参照するものではなく、図の真ん中にあるAPI Gatewayを介してアクセスしています。このAPI Gatewayにおいて、プロトコルの変換や、アクセストークンの検証の部分、アクセス制御などを行なっています。
このような状態でメルカリ/メルペイの機能を使った新しいサービスを作ろうとしたとき、現状は特定のクライアント専用のAPI Gatewayを新しく作る形をとっています。
この図で言うと、クライアントXが出てきたらAPI Gatewayfor X ができます。API Gateway for Xでは、クライアントXが利用したい機能を持っているMicroserviceへのルートだけを作っています。
API Gateway for XはクライアントXにしか使われてほしくないものです。どうやって制限するかというと、クライアントXといった名前が入ったScopeをアクセストークンの中に入れて、そのアクセストークンがそのScopeを持っていたらAPI Gatewayを通す方法をとっています。この形は、多くの課題を抱えています。
課題
たとえば、新しいサービスができるたびにAPI Gatewayを新しく作らなければならず、追加コストや管理コストがどんどん増えていきます。そのコストはクライアント側の開発速度にも影響します。
アクセス制限の粒度が粗いことも問題です。この形で仮にAPI Gatewayを統一して、不特定多数のクライアントにGatewayを使わせると、クライアント側が必要がない機能まで使える状態になってしまいます。
現状ではGatewayにアクセスできるかどうかだけを制限対象としていて、Gatewayを介してどのような機能を使いたいかという粒度でのアクセス制限をできている状態ではありません。
さらに、Data Protectionについてもいくつか問題があります。一つはInterfaceの問題です。
今の形では、MicroserviceのInterfaceをAPI Gatewayに返してそのままクライアントに使わせる状態になっています。ただ、Microserviceが提供しているAPIは基本的に同じカンパニー内の他のMicroserviceに利用されることを想定して作られています。外部のサービスが利用することは想定されていません。
そのため、たとえばどの権限を持った人がどのリソースや個人情報にアクセスしていいかという制限は、MicroserviceのInterfaceでは提供されません。よって外部に提供するAPIとして十分なアクセス制御・リソース制限ができていない状態になります。
またuser_idについても問題があります。クライアントがアクセストークンやIDトークンを取得するときには、OIDCのプロセスに沿うため、サーバー内部で使っているuser_idをそのまま取得しなくてもいい状態になっています。ですので、OIDCでいうところのPPIDというものをIDPが発行し、クライアントはそれをもってユーザーを識別しています。
ただ、公開されているAPIがMicroserviceのInterfaceと同じものなので、そのInterfaceの中には、リクエストの一つのパラメータとしてuser_idを指定しなければならない場合もあります。クライアントがそのAPIを使うためには、サーバー内部で使われているuser_idを知る必要があり、PPIDがそのAPIを使えないからクライアントXもしくはYでも、サーバーと同じuser_idを使う状態になっています。
これが起きると、PPIDの目的であるRPトラッキングを防げなくなる上、異なる組織で保存している個人情報を容易に紐付けられるようになってしまいます。
それぞれの組織でuser_idに基づいて個人情報を保存しており、その情報にアクセスするためのアクセスコントロールは基本的にされていると思います。ですが、仮に一つの組織から個人情報が漏えいしたときや両方の組織に対するアクセス権を持っているユーザーがいた場合、それぞれの情報を同じuser_idを使って保存しているとそれぞれの情報を容易に結びつけられて、それぞれのカンパニーで想定している以上のダメージを受ける可能性があります。
それぞれのデータにアクセスできることと、それぞれのデータを同じ人間のデータとして結びつけられることは別の権限であると考えるのが基本的な方向性なのですが、同じuser_idを使うことで、それをできなくなるという問題があります。
これらが、Relying Partyに関するアクセス制御とData Protectionの現状と課題です。
共通のAPI
これらの状況を解消するために今後やっていきたいことは、まず共通のAPIを提供することです。
このAPIは、不特定多数のRPが利用可能なものとして提供しています。外部向けのAPIとして意識しなければならないことを満たしています。
外部向けAPIとして意識しなければならないことは、まず利用可能なAPI GatewayやAPIの制限ができる状態です。ここでは「共通のAPI」と言ってはいますが、基本的に特定のクライアントに対してAPIを提供することを否定するものではありません。
たとえば1st party Clientが利用するAPIは、汎用的なものよりも、1st party のユースケースに特化したもののほうが適切だと考えています。また、API Gatewayは複数存在するのが基本的な構造です。
そのため、クライアントXに払い出すアクセストークンがどのGatewayにアクセス可能かは、適切に制限する必要があります。また、一つのAPI Gatewayを不特定多数のクライアントで使うことになるので、昨日や権限の制限をする必要が出てきます。
このような共通のAPIを作るためには、IDPだけではなく各カンパニーのアーキテクチャや各APIを提供しているプロダクトチームと議論し、どういう形がメルカリグループの現状・将来像とマッチしているかを検討していく必要があります。その中でIDPとして提供していく必要がある機能を考えます。
IDPチームとして提供すること
たとえば、利用可能なAPI Gateway/APIの制御やリソースを制限する方法を考えることになります。
API Gateway/API制限では、基本的にアクセストークンの権限制御はトークン内のクレームによって行うのが一般的かと思います。しかし、どのような粒度でどのようなScopeを作り、権限を制御するかは実装上の問題で、標準仕様の範囲外になります。
現状は、Audienceを使ってGatewayを制限して、Scopeを使ってどのAPIを利用できるかを制限しようとしています。ScopeによってどのAPIが利用可能かは、APIを公開するチームが自分で管理できる形にします。それによってScopeの粒度やフォーマット、Scope自体が何を表現しているものなのかの一貫性を保てるようになると考えています。そのため、全社共通のこのScope体系は今のところ作るつもりはなく、ScopeはAPI Gatewayに閉じた形で定義することを考えています。
以上、Relying Partyにおけるアクセス制御とData Protectionの話でした。
Resource Serverへのアクセス制御・Data Protection
最後に、Resource Serverへのアクセス制御・Data Protectionの話をしていきたいと思います。
どこの話か?
これは、Relying Partyと同じようにメルカリ/メルペイの一部の機能を使って新しいサービスを作り、そのサービスがメルカリグループの一つの機能として提供される、かつメルカリ/メルペイとはセキュリティ的・データガバナンス的に境界を設けたいときの話です。
同じメルカリグループだけど、提供しているカンパニーがメルカリ/メルペイとは違うのでセキュリティ的に境界を設けたいといった例があげられます。
現状はどうなっているか?
現状を説明するために、メルカリ/メルペイのサーバー内部のアクセス制御についてお話しします。
Microserviceの構成については先ほどお話ししましたが、今度はどのようなトークンが使われているかを軸にして話します。
まず、クライアントアプリケーションはIdPからいろいろなGrant Typeを経由してアクセストークンを取得します。そのアクセストークンを使って、メルカリのAPIを叩きます。アクセストークンは内部的にはPFATと呼ばれています。
アクセストークンをまずGatewayが受け取って、そのトークンが有効であるかどうかを検証します。同じタイミングで各Microserviceに対してリクエストを投げるときのPATと呼ばれる内部的なトークンを一緒に作ります。
このPFATは基本的にJWT(JSON Web Token)のフォーマットを取っていて、どこのエンドポイントにリクエストが来たか、誰が認証したかといった情報をJWTの中に含めて、Microserviceに送ります。リクエストのコンテキスト情報をMicroserviceで伝えることが、基本的な役割です。
この各Microserviceでは、最低限PFATの検証をしてリクエストを認証している状態です。この状態において各Microserviceで行われるアクセス制御は、大きく2パターンあります。
一つ目はPATのClaimsによるアクセス制御です。たとえばPATのサブジェクトがユーザーであれば、そのユーザーのリソースしか触らせない制御方法です。もう一つは、Microserviceの構造に基づいた制御です。たとえばネットワークポリシーによる制御やサービス化認証による制限をいいます。
どちらの場合にしてもリクエストを送ってきたエンドポイントがどこか、自分にリクエストを送っている直接のMicroserviceがどこかといった、ドメインのサービス構成についてある程度の知識を持って制限するものになります。
課題
このような状況で境界を持った別ドメインのMicroservice群を同じ環境に構築しようとすると、図のようになると思います。
まず上に追加されているのが、新しいビジネスドメインのMicroservice群です。そこにアクセスするとき、同じくクライアントはPFATを使っているGatewayにアクセスして、GatewayはPATを発行して、新しいビジネスメインのMicroserviceにリクエストを送信します。新しいビジネスドメイン側のMicroserviceが、メルカリ/メルペイのMicroserviceにアクセスしたいときには、メルカリペイ内でMicroservice間の通信をPATで行っているのと同じように、PATを使ってアクセスする形に自然となっていくと思います。ただこの形にすると、アクセス制御とData Protectionにおいて課題が発生してきます。
まず、アクセス制御に関しては、PATのClaimでアクセス制御するか、アクセス元のMicroserviceを限定する形になると思います。どちらの場合においても、メルカリ/メルペイ側のMicroserviceで新しいビジネスドメイン側の状況やどのMicroserviceがリクエストを送ってくるかの情報を知る必要があります。
提供しているカンパニーが違い、セキュリティ的・データガバナンス的な境界を設けたい状況においては、それぞれのMicroserviceを管理しているチーム間で密にコミュニケーションを取ることは基本的にはしたくないものです。ですので、それぞれの相手のドメインの知識を知らないとアクセス制限できない状況は、基本的に要求にマッチしていない状況だと考えています。
またData Protectionに関しても、Relying Partyと同じ問題が発生します。つまり、Interfaceとuser_idの問題です。Interfaceに関して言うと、MicroserviceのInterfaceをそのまま使うことになるので、リソースにアクセス可能な権限を適切に管理できる状況ではありません。
また同じInterfaceの中に、user_idを要求しているものがあればリクエストを送る側はuser_idを知る必要が出てくるので、メルカリ/メルペイも新しいビジネスドメインも同じuser_idを使ってデータを保存することが必要です。
これがResource Serverにおけるアクセス制御とData Protectionの現状と課題です。これらの状況を解決するために今後やっていきたいこととしては、Resource Serverを分離する方法と、先ほど出てきた共有のAPIを使ってResource Server間の通信をすることがあげられます。
Resource Serverの分離
まずResource Server間の分離の話です。これはPATの署名に利用する鍵を変更することで対応しようと思います。こちらの図の先ほどとの違いは、Gatewayがわかれている点です。
流れとしては、クライアントがPATを取得して、そのPATを持って新しいビジネスドメイン側のGatewayにアクセスしに行きます。
新しいビジネスドメイン側では、メルカリ/メルペイとは異なる秘密鍵を使ってJWTの署名をしてPATを作ってMicroserviceに流します。新しいビジネスドメイン側では図上の緑のPATができて、メルカリ/メルペイの青のPATとは別の状況になるという感じです。
この状態にしておくと、仮に新しいビジネスドメイン側の緑のPATをなんとかして取得して、メルカリ/メルペイのMicroserviceにアクセスしようとしても、メルカリ/メルペイ側のMicroserviceが期待しているPATの署名に使われている鍵は緑ではなく青なので、直接通信できない状況になって、明確な境界を作ることができます。これで分離自体はできるようになります。
Resource Server間での通信
どのようにResource Server間での通信を行うかといいますと、Relying Partyのときに出てきた共通APIを使います。共通APIを利用することで、基本的にメルカリ/メルペイ側は、新しいビジネスドメイン側の詳細を知ることはなく、メルカリ/メルペイが適切だと考えている粒度でのアクセス制限をできるようになります。
あとData Protectionにおいても、Relying Partyと同じように、リソースにアクセスするために必要な権限を持っているかどうかの確認をできるようになります。
Resource Server間のuser_idの分離
共通APIを使うことのもう一つのいいところは、user_idの分離もできることです。
Relying Partyのときに説明したように、共通APIの外側ではPPIDを使ってやりとりすることになるので、新しいビジネスドメイン側ではPPIDを使ってデータ保存するか、それとも内部で別のuser_idを発行して、そのPPIDのコネクションを保存するかという2択になります。新しいビジネスドメイン側の判断になっていきます。
ただ基本的に内部のuser_idを外部に露出しない方がいいと考えているので、PPIDそのものを保存するよりも、内部のuser_idとPPIDのマッピングを保存する方が適切だと思います。
以上がResource Serverにおけるアクセス制御Data Protectionの話でした。
まとめ
以上が現状のIDPにまつわるトピックの状況です。今回はだいぶ駆け足だったのですが、今後それぞれのトピックがさらに具体的になりましたら、狭く深く話せたらいいなと思います。
ご清聴ありがとうございました。